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