Coverage Report

Created: 2023-03-26 07:17

/src/pdns/pdns/unix_utility.cc
Line
Count
Source (jump to first uncovered line)
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
#ifdef NEED_INET_NTOP_PROTO
40
extern "C" {
41
const char *inet_ntop(int af, const void *src, char *dst, size_t cnt);
42
}
43
#endif
44
45
46
#include "namespaces.hh"
47
48
49
// Connects to socket with timeout
50
int Utility::timed_connect( Utility::sock_t sock,
51
    const sockaddr *addr,
52
    Utility::socklen_t sockaddr_size,
53
    int timeout_sec,
54
    int timeout_usec )
55
0
{
56
0
  fd_set set;
57
0
  struct timeval timeout;
58
0
  int ret;
59
60
0
  timeout.tv_sec = timeout_sec;
61
0
  timeout.tv_usec = timeout_usec;
62
63
0
  FD_ZERO(&set);
64
0
  FD_SET(sock, &set);
65
66
0
  setNonBlocking(sock);
67
68
0
  if ((ret = connect (sock, addr, sockaddr_size)) < 0) {
69
0
    if (errno != EINPROGRESS)
70
0
      return ret;
71
0
  }
72
73
0
  ret = select(sock + 1, nullptr, &set, nullptr, &timeout);
74
0
  setBlocking(sock);
75
76
0
  return ret;
77
0
}
78
79
80
81
void Utility::setBindAny(int af, sock_t sock)
82
0
{
83
0
  const int one = 1;
84
85
0
  (void) one; // avoids 'unused var' warning on systems that have none of the defines checked below
86
0
#ifdef IP_FREEBIND
87
0
  if (setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) {
88
0
    int err = errno;
89
0
    SLOG(g_log<<Logger::Warning<<"Warning: IP_FREEBIND setsockopt failed: "<<stringerror(err)<<endl,
90
0
         g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: IP_FREEBIND setsockopt failed"));
91
0
  }
92
0
#endif
93
94
#ifdef IP_BINDANY
95
  if (af == AF_INET)
96
    if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) < 0) {
97
      int err = errno;
98
      SLOG(g_log<<Logger::Warning<<"Warning: IP_BINDANY setsockopt failed: "<<stringerror(err)<<endl,
99
           g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: IP_BINDANY setsockopt failed"));
100
    }
101
#endif
102
#ifdef IPV6_BINDANY
103
  if (af == AF_INET6) {
104
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) < 0) {
105
      int err = errno;
106
      SLOG(g_log<<Logger::Warning<<"Warning: IPV6_BINDANY setsockopt failed: "<<stringerror(err)<<endl,
107
           g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: IPV6_BINDANY setsockopt failed"));
108
    }
109
  }
110
#endif
111
#ifdef SO_BINDANY
112
  if (setsockopt(sock, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) < 0) {
113
    int err = errno;
114
    SLOG(g_log<<Logger::Warning<<"Warning: SO_BINDANY setsockopt failed: "<<stringerror(err)<<endl,
115
         g_slog->withName("runtime")->error(Logr::Warning, err, "Warning: SO_BINDANY setsockopt failed"));
116
  }
117
#endif
118
0
}
119
120
const char *Utility::inet_ntop(int af, const char *src, char *dst, size_t size)
121
0
{
122
0
  return ::inet_ntop(af,src,dst,size);
123
0
}
124
125
unsigned int Utility::sleep(unsigned int sec)
126
0
{
127
0
  return ::sleep(sec);
128
0
}
129
130
void Utility::usleep(unsigned long usec)
131
0
{
132
0
  struct timespec ts;
133
0
  ts.tv_sec = usec / 1000000;
134
0
  ts.tv_nsec = (usec % 1000000) * 1000;
135
  // POSIX.1 recommends using nanosleep instead of usleep
136
0
  ::nanosleep(&ts, nullptr);
137
0
}
138
139
140
// Drops the program's group privileges.
141
void Utility::dropGroupPrivs( uid_t uid, gid_t gid )
142
0
{
143
0
  if(gid && gid != getegid()) {
144
0
    if(setgid(gid)<0) {
145
0
      int err = errno;
146
0
      SLOG(g_log<<Logger::Critical<<"Unable to set effective group id to "<<gid<<": "<<stringerror(err)<<endl,
147
0
           g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to set effective group id", "gid", Logging::Loggable(gid)));
148
0
      exit(1);
149
0
    }
150
0
    else {
151
0
      SLOG(g_log<<Logger::Info<<"Set effective group id to "<<gid<<endl,
152
0
           g_slog->withName("runtime")->info(Logr::Info, "Set effective group id", "gid", Logging::Loggable(gid)));
153
0
    }
154
0
    struct passwd *pw=getpwuid(uid);
155
0
    if(!pw) {
156
0
      SLOG(g_log<<Logger::Warning<<"Unable to determine user name for uid "<<uid<<endl,
157
0
           g_slog->withName("runtime")->info(Logr::Warning, "Unable to determine user name", "uid", Logging::Loggable(uid)));
158
0
      if (setgroups(0, nullptr)<0) {
159
0
        int err = errno;
160
0
        SLOG(g_log<<Logger::Critical<<"Unable to drop supplementary gids: "<<stringerror(err)<<endl,
161
0
             g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to drop supplementary gids"));
162
0
        exit(1);
163
0
      }
164
0
    } else {
165
0
      if (initgroups(pw->pw_name, gid)<0) {
166
0
        int err = errno;
167
0
        SLOG(g_log<<Logger::Critical<<"Unable to set supplementary groups: "<<stringerror(err)<<endl,
168
0
             g_slog->withName("runtime")->error(Logr::Critical, err, "Unable to drop supplementary groups"));
169
0
        exit(1);
170
0
      }
171
0
    }
172
0
  }
173
0
}
174
175
176
// Drops the program's user privileges.
177
void Utility::dropUserPrivs( uid_t uid )
178
0
{
179
0
  if(uid && uid != geteuid()) {
180
0
    if(setuid(uid)<0) {
181
0
      int err = errno;
182
0
      SLOG(g_log<<Logger::Critical<<"Unable to set effective user id to "<<uid<<": "<<stringerror(err)<<endl,
183
0
           g_slog->withName("runtime")->error(Logr::Error, err, "Unable to set effective user id", "uid", Logging::Loggable(uid)));
184
0
      exit(1);
185
0
    }
186
0
    else {
187
0
      SLOG(g_log<<Logger::Info<<"Set effective user id to "<<uid<<endl,
188
0
           g_slog->withName("runtime")->info("Set effective user", "uid", Logging::Loggable(uid)));
189
0
    }
190
0
  }
191
0
}
192
193
194
// Returns the current process id.
195
Utility::pid_t Utility::getpid( )
196
0
{
197
0
  return ::getpid();
198
0
}
199
200
201
// Returns the current time.
202
int Utility::gettimeofday( struct timeval *tv, void *tz )
203
0
{
204
0
  return ::gettimeofday(tv,nullptr);
205
0
}
206
207
// Sets the random seed.
208
void Utility::srandom()
209
0
{
210
0
  struct timeval tv;
211
0
  gettimeofday(&tv, nullptr);
212
0
  ::srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
213
0
}
214
215
// Writes a vector.
216
int Utility::writev(int socket, const iovec *vector, size_t count )
217
0
{
218
0
  return ::writev(socket,vector,count);
219
0
}
220
221
/* this is cut and pasted from dietlibc, gratefully copied! */
222
0
static int isleap(int year) {
223
  /* every fourth year is a leap year except for century years that are
224
   * not divisible by 400. */
225
0
  return (!(year%4) && ((year%100) || !(year%400)));
226
0
}
227
228
time_t Utility::timegm(struct tm *const t)
229
0
{
230
0
  const static short spm[13] = /* days per month -- nonleap! */
231
0
  { 0,
232
0
    (31),
233
0
    (31+28),
234
0
    (31+28+31),
235
0
    (31+28+31+30),
236
0
    (31+28+31+30+31),
237
0
    (31+28+31+30+31+30),
238
0
    (31+28+31+30+31+30+31),
239
0
    (31+28+31+30+31+30+31+31),
240
0
    (31+28+31+30+31+30+31+31+30),
241
0
    (31+28+31+30+31+30+31+31+30+31),
242
0
    (31+28+31+30+31+30+31+31+30+31+30),
243
0
    (31+28+31+30+31+30+31+31+30+31+30+31),
244
0
  };
245
246
0
  time_t  day;
247
0
  time_t  i;
248
0
  time_t years = t->tm_year - 70;
249
250
0
  if (t->tm_sec>60) { t->tm_min += t->tm_sec/60; t->tm_sec%=60; }
251
0
  if (t->tm_min>60) { t->tm_hour += t->tm_min/60; t->tm_min%=60; }
252
0
  if (t->tm_hour>60) { t->tm_mday += t->tm_hour/60; t->tm_hour%=60; }
253
0
  if (t->tm_mon>11) { t->tm_year += t->tm_mon/12; t->tm_mon%=12; }
254
255
0
  while (t->tm_mday>spm[1+t->tm_mon]) {
256
0
    if (t->tm_mon==1 && isleap(t->tm_year+1900)) {
257
0
      if (t->tm_mon==31+29) break;
258
0
      --t->tm_mday;
259
0
    }
260
0
    t->tm_mday-=spm[t->tm_mon];
261
0
    ++t->tm_mon;
262
0
    if (t->tm_mon>11) { t->tm_mon=0; ++t->tm_year; }
263
0
  }
264
265
0
  if (t->tm_year < 70)
266
0
    return (time_t) -1;
267
  /* Days since 1970 is 365 * number of years + number of leap years since 1970 */
268
0
  day  = years * 365 + (years + 1) / 4;
269
270
  /* After 2100 we have to subtract 3 leap years for every 400 years
271
     This is not intuitive. Most mktime implementations do not support
272
     dates after 2059, anyway, so we might leave this out for its
273
     bloat. */
274
0
  if ((years -= 131) >= 0) {
275
0
    years /= 100;
276
0
    day -= (years >> 2) * 3 + 1;
277
0
    if ((years &= 3) == 3) years--;
278
0
    day -= years;
279
0
  }
280
281
0
  day += t->tm_yday = spm [t->tm_mon] + t->tm_mday-1 + ( isleap (t->tm_year+1900)  &  (t->tm_mon > 1) );
282
283
  /* day is now the number of days since 'Jan 1 1970' */
284
0
  i = 7;
285
0
  t->tm_wday = (day + 4) % i;                        /* Sunday=0, Monday=1, ..., Saturday=6 */
286
287
0
  i = 24;
288
0
  day *= i;
289
0
  i = 60;
290
0
  return ((day + t->tm_hour) * i + t->tm_min) * i + t->tm_sec;
291
0
}