Coverage Report

Created: 2026-08-31 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/unix_utility.cc
Line
Count
Source
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "utility.hh"
26
#include <cstring>
27
#include <fcntl.h>
28
#include <unistd.h>
29
#include <stdlib.h>
30
#include "pdnsexception.hh"
31
#include "logger.hh"
32
#include "logging.hh"
33
#include "misc.hh"
34
#include <pwd.h>
35
#include <grp.h>
36
#include <sys/types.h>
37
#include <sys/select.h>
38
39
#include "namespaces.hh"
40
41
42
// Connects to socket with timeout
43
int Utility::timed_connect( Utility::sock_t sock,
44
    const sockaddr *addr,
45
    Utility::socklen_t sockaddr_size,
46
    int timeout_sec,
47
    int timeout_usec )
48
0
{
49
0
  fd_set set;
50
0
  struct timeval timeout;
51
0
  int ret;
52
53
0
  timeout.tv_sec = timeout_sec;
54
0
  timeout.tv_usec = timeout_usec;
55
56
0
  FD_ZERO(&set);
57
0
  FD_SET(sock, &set);
58
59
0
  setNonBlocking(sock);
60
61
0
  if ((ret = connect (sock, addr, sockaddr_size)) < 0) {
62
0
    if (errno != EINPROGRESS)
63
0
      return ret;
64
0
  }
65
66
0
  ret = select(sock + 1, nullptr, &set, nullptr, &timeout);
67
0
  setBlocking(sock);
68
69
0
  return ret;
70
0
}
71
72
73
74
void Utility::setBindAny([[maybe_unused]] int af, [[maybe_unused]] sock_t sock)
75
0
{
76
0
  const int one = 1;
77
78
0
  (void) one; // avoids 'unused var' warning on systems that have none of the defines checked below
79
0
#ifdef IP_FREEBIND
80
0
  if (setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) {
81
0
    int err = errno;
82
0
    SLOG(g_log<<Logger::Warning<<"Warning: IP_FREEBIND setsockopt failed: "<<stringerror(err)<<endl,
83
0
         g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: IP_FREEBIND setsockopt failed"));
84
0
  }
85
0
#endif
86
87
#ifdef IP_BINDANY
88
  if (af == AF_INET)
89
    if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) < 0) {
90
      int err = errno;
91
      SLOG(g_log<<Logger::Warning<<"Warning: IP_BINDANY setsockopt failed: "<<stringerror(err)<<endl,
92
           g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: IP_BINDANY setsockopt failed"));
93
    }
94
#endif
95
#ifdef IPV6_BINDANY
96
  if (af == AF_INET6) {
97
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) < 0) {
98
      int err = errno;
99
      SLOG(g_log<<Logger::Warning<<"Warning: IPV6_BINDANY setsockopt failed: "<<stringerror(err)<<endl,
100
           g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: IPV6_BINDANY setsockopt failed"));
101
    }
102
  }
103
#endif
104
#ifdef SO_BINDANY
105
  if (setsockopt(sock, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) < 0) {
106
    int err = errno;
107
    SLOG(g_log<<Logger::Warning<<"Warning: SO_BINDANY setsockopt failed: "<<stringerror(err)<<endl,
108
         g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: SO_BINDANY setsockopt failed"));
109
  }
110
#endif
111
0
}
112
113
unsigned int Utility::sleep(unsigned int sec)
114
0
{
115
0
  return ::sleep(sec);
116
0
}
117
118
void Utility::usleep(unsigned long usec)
119
0
{
120
0
  struct timespec ts;
121
0
  ts.tv_sec = usec / 1000000;
122
0
  ts.tv_nsec = (usec % 1000000) * 1000;
123
  // POSIX.1 recommends using nanosleep instead of usleep
124
0
  ::nanosleep(&ts, nullptr);
125
0
}
126
127
128
// Drops the program's group privileges.
129
void Utility::dropGroupPrivs( uid_t uid, gid_t gid )
130
0
{
131
0
  if(gid && gid != getegid()) {
132
0
    if(setgid(gid)<0) {
133
0
      int err = errno;
134
0
      SLOG(g_log<<Logger::Critical<<"Unable to set effective group id to "<<gid<<": "<<stringerror(err)<<endl,
135
0
           g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to set effective group id", "gid", Logging::Loggable(gid)));
136
0
      exit(1);
137
0
    }
138
0
    else {
139
0
      SLOG(g_log<<Logger::Info<<"Set effective group id to "<<gid<<endl,
140
0
           g_slog->withName("runtime")->info(Logr::Info, "Set effective group id", "gid", Logging::Loggable(gid)));
141
0
    }
142
0
    struct passwd *pw=getpwuid(uid);
143
0
    if(!pw) {
144
0
      SLOG(g_log<<Logger::Warning<<"Unable to determine user name for uid "<<uid<<endl,
145
0
           g_slog->withName("runtime")->info(Logr::Warning, "Unable to determine user name", "uid", Logging::Loggable(uid)));
146
0
      if (setgroups(0, nullptr)<0) {
147
0
        int err = errno;
148
0
        SLOG(g_log<<Logger::Critical<<"Unable to drop supplementary gids: "<<stringerror(err)<<endl,
149
0
             g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to drop supplementary gids"));
150
0
        exit(1);
151
0
      }
152
0
    } else {
153
0
      if (initgroups(pw->pw_name, gid)<0) {
154
0
        int err = errno;
155
0
        SLOG(g_log<<Logger::Critical<<"Unable to set supplementary groups: "<<stringerror(err)<<endl,
156
0
             g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to set supplementary groups"));
157
0
        exit(1);
158
0
      }
159
0
    }
160
0
  }
161
0
}
162
163
164
// Drops the program's user privileges.
165
void Utility::dropUserPrivs( uid_t uid )
166
0
{
167
0
  if(uid && uid != geteuid()) {
168
0
    if(setuid(uid)<0) {
169
0
      int err = errno;
170
0
      SLOG(g_log<<Logger::Critical<<"Unable to set effective user id to "<<uid<<": "<<stringerror(err)<<endl,
171
0
           g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to set effective user id", "uid", Logging::Loggable(uid)));
172
0
      exit(1);
173
0
    }
174
0
    else {
175
0
      SLOG(g_log<<Logger::Info<<"Set effective user id to "<<uid<<endl,
176
0
           g_slog->withName("runtime")->info(Logr::Info, "Set effective user", "uid", Logging::Loggable(uid)));
177
0
    }
178
0
  }
179
0
}
180
181
182
// Returns the current process id.
183
Utility::pid_t Utility::getpid( )
184
0
{
185
0
  return ::getpid();
186
0
}
187
188
189
// Returns the current time.
190
int Utility::gettimeofday( struct timeval *tv, void * /* tz */)
191
0
{
192
0
  return ::gettimeofday(tv, nullptr);
193
0
}
194
195
// Writes a vector.
196
int Utility::writev(int socket, const iovec *vector, size_t count )
197
0
{
198
0
  return ::writev(socket,vector,count);
199
0
}
200
201
/* this is cut and pasted from dietlibc, gratefully copied! */
202
43
static int isleap(int year) {
203
  /* every fourth year is a leap year except for century years that are
204
   * not divisible by 400. */
205
43
  return (!(year%4) && ((year%100) || !(year%400)));
206
43
}
207
208
time_t Utility::timegm(struct tm *const t)
209
63
{
210
63
  const static short spm[13] = /* days per month -- nonleap! */
211
63
  { 0,
212
63
    (31),
213
63
    (31+28),
214
63
    (31+28+31),
215
63
    (31+28+31+30),
216
63
    (31+28+31+30+31),
217
63
    (31+28+31+30+31+30),
218
63
    (31+28+31+30+31+30+31),
219
63
    (31+28+31+30+31+30+31+31),
220
63
    (31+28+31+30+31+30+31+31+30),
221
63
    (31+28+31+30+31+30+31+31+30+31),
222
63
    (31+28+31+30+31+30+31+31+30+31+30),
223
63
    (31+28+31+30+31+30+31+31+30+31+30+31),
224
63
  };
225
226
63
  time_t  day;
227
63
  time_t  i;
228
63
  time_t years = t->tm_year - 70;
229
230
  // We allow for some benign out-of-range values
231
63
  if (t->tm_sec>60) { t->tm_min += t->tm_sec/60; t->tm_sec%=60; }
232
63
  if (t->tm_min>60) { t->tm_hour += t->tm_min/60; t->tm_min%=60; }
233
63
  if (t->tm_hour>24) { t->tm_mday += t->tm_hour/24; t->tm_hour%=24; }
234
63
  if (t->tm_mon>11) { t->tm_year += t->tm_mon/12; t->tm_mon%=12; }
235
236
63
  while (t->tm_mday>spm[1+t->tm_mon]) {
237
0
    if (t->tm_mon==1 && isleap(t->tm_year+1900)) {
238
0
      if (t->tm_mon==31+29) break;
239
0
      --t->tm_mday;
240
0
    }
241
0
    t->tm_mday-=spm[t->tm_mon];
242
0
    ++t->tm_mon;
243
0
    if (t->tm_mon>11) { t->tm_mon=0; ++t->tm_year; }
244
0
  }
245
246
63
  if (t->tm_year < 70)
247
20
    return (time_t) -1;
248
  /* Days since 1970 is 365 * number of years + number of leap years since 1970 */
249
43
  day  = years * 365 + (years + 1) / 4;
250
251
  /* After 2100 we have to subtract 3 leap years for every 400 years
252
     This is not intuitive. Most mktime implementations do not support
253
     dates after 2059, anyway, so we might leave this out for its
254
     bloat. */
255
43
  if ((years -= 131) >= 0) {
256
31
    years /= 100;
257
31
    day -= (years >> 2) * 3 + 1;
258
31
    if ((years &= 3) == 3) years--;
259
31
    day -= years;
260
31
  }
261
262
43
  day += t->tm_yday = spm [t->tm_mon] + t->tm_mday-1 + ( isleap (t->tm_year+1900)  &  (t->tm_mon > 1) );
263
264
  /* day is now the number of days since 'Jan 1 1970' */
265
43
  i = 7;
266
  // coverity[store_truncates_time_t]
267
43
  t->tm_wday = (day + 4) % i;                        /* Sunday=0, Monday=1, ..., Saturday=6 */
268
269
43
  i = 24;
270
43
  day *= i;
271
43
  i = 60;
272
43
  return ((day + t->tm_hour) * i + t->tm_min) * i + t->tm_sec;
273
63
}