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