/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 | } |