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