/src/net-snmp/snmplib/system.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * system.c |
3 | | */ |
4 | | /* Portions of this file are subject to the following copyright(s). See |
5 | | * the Net-SNMP's COPYING file for more details and other copyrights |
6 | | * that may apply: |
7 | | */ |
8 | | /*********************************************************** |
9 | | Copyright 1992 by Carnegie Mellon University |
10 | | |
11 | | All Rights Reserved |
12 | | |
13 | | Permission to use, copy, modify, and distribute this software and its |
14 | | documentation for any purpose and without fee is hereby granted, |
15 | | provided that the above copyright notice appear in all copies and that |
16 | | both that copyright notice and this permission notice appear in |
17 | | supporting documentation, and that the name of CMU not be |
18 | | used in advertising or publicity pertaining to distribution of the |
19 | | software without specific, written prior permission. |
20 | | |
21 | | CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
22 | | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
23 | | CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
24 | | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
25 | | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
26 | | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
27 | | SOFTWARE. |
28 | | ******************************************************************/ |
29 | | /* |
30 | | * Portions of this file are copyrighted by: |
31 | | * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. |
32 | | * Use is subject to license terms specified in the COPYING file |
33 | | * distributed with the Net-SNMP package. |
34 | | */ |
35 | | /* |
36 | | * Portions of this file are copyrighted by: |
37 | | * Copyright (C) 2007 Apple, Inc. All rights reserved. |
38 | | * Use is subject to license terms specified in the COPYING file |
39 | | * distributed with the Net-SNMP package. |
40 | | * |
41 | | * Portions of this file are copyrighted by: |
42 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
43 | | * Use is subject to license terms specified in the COPYING file |
44 | | * distributed with the Net-SNMP package. |
45 | | */ |
46 | | /* |
47 | | * System dependent routines go here |
48 | | */ |
49 | | #include <net-snmp/net-snmp-config.h> |
50 | | #include <net-snmp/net-snmp-features.h> |
51 | | #include <stdio.h> |
52 | | #include <ctype.h> |
53 | | #include <errno.h> |
54 | | |
55 | | #ifdef HAVE_INTTYPES_H |
56 | | #include <inttypes.h> |
57 | | #endif |
58 | | #ifdef HAVE_IO_H |
59 | | #include <io.h> |
60 | | #endif |
61 | | #ifdef HAVE_DIRECT_H |
62 | | #include <direct.h> |
63 | | #endif |
64 | | #ifdef HAVE_UNISTD_H |
65 | | #include <unistd.h> |
66 | | #endif |
67 | | #ifdef HAVE_STDLIB_H |
68 | | #include <stdlib.h> |
69 | | #endif |
70 | | |
71 | | #ifdef TIME_WITH_SYS_TIME |
72 | | # include <sys/time.h> |
73 | | # include <time.h> |
74 | | #else |
75 | | # ifdef HAVE_SYS_TIME_H |
76 | | # include <sys/time.h> |
77 | | # else |
78 | | # include <time.h> |
79 | | # endif |
80 | | #endif |
81 | | |
82 | | #include <sys/types.h> |
83 | | |
84 | | #ifdef HAVE_NETINET_IN_H |
85 | | #include <netinet/in.h> |
86 | | #endif |
87 | | |
88 | | #ifdef HAVE_SYS_SOCKET_H |
89 | | #include <sys/socket.h> |
90 | | #endif |
91 | | #ifdef HAVE_NET_IF_H |
92 | | #include <net/if.h> |
93 | | #endif |
94 | | #ifdef HAVE_NETDB_H |
95 | | #include <netdb.h> |
96 | | #endif |
97 | | |
98 | | |
99 | | #ifdef HAVE_SYS_SOCKIO_H |
100 | | #include <sys/sockio.h> |
101 | | #endif |
102 | | |
103 | | #ifdef HAVE_SYS_IOCTL_H |
104 | | #include <sys/ioctl.h> |
105 | | #endif |
106 | | |
107 | | #ifdef HAVE_NLIST_H |
108 | | #include <nlist.h> |
109 | | #endif |
110 | | |
111 | | #ifdef HAVE_SYS_FILE_H |
112 | | #include <sys/file.h> |
113 | | #endif |
114 | | |
115 | | #ifdef HAVE_KSTAT_H |
116 | | #include <kstat.h> |
117 | | #endif |
118 | | |
119 | | #ifdef HAVE_SYS_PARAM_H |
120 | | #include <sys/param.h> |
121 | | #endif |
122 | | #ifdef HAVE_SYS_SYSCTL_H |
123 | | #include <sys/sysctl.h> |
124 | | #endif |
125 | | |
126 | | #ifdef HAVE_STRING_H |
127 | | #include <string.h> |
128 | | #else |
129 | | #include <strings.h> |
130 | | #endif |
131 | | |
132 | | #ifdef WIN32 |
133 | | #include <wchar.h> /* wcsncmp() */ |
134 | | #include <winperf.h> /* PERF_DATA_BLOCK */ |
135 | | #endif |
136 | | |
137 | | #ifdef HAVE_SYS_STAT_H |
138 | | #include <sys/stat.h> |
139 | | #endif |
140 | | #ifdef HAVE_FCNTL_H |
141 | | #include <fcntl.h> |
142 | | #endif |
143 | | |
144 | | #if defined(hpux10) || defined(hpux11) |
145 | | #include <sys/pstat.h> |
146 | | #endif |
147 | | |
148 | | #ifdef HAVE_SYS_UTSNAME_H |
149 | | #include <sys/utsname.h> |
150 | | #endif |
151 | | |
152 | | #ifdef HAVE_SYS_SYSTEMCFG_H |
153 | | #include <sys/systemcfg.h> |
154 | | #endif |
155 | | |
156 | | #ifdef HAVE_SYS_SYSTEMINFO_H |
157 | | #include <sys/systeminfo.h> |
158 | | #endif |
159 | | |
160 | | #ifdef HAVE_CRT_EXTERNS_H |
161 | | #include <crt_externs.h> /* for _NSGetArgv() */ |
162 | | #endif |
163 | | |
164 | | #ifdef HAVE_MACH_O_DYLD_H |
165 | | #include <mach-o/dyld.h> |
166 | | #endif |
167 | | |
168 | | #ifdef HAVE_PWD_H |
169 | | #include <pwd.h> |
170 | | #endif |
171 | | #ifdef HAVE_GRP_H |
172 | | #include <grp.h> |
173 | | #endif |
174 | | |
175 | | #ifdef HAVE_LIMITS_H |
176 | | #include <limits.h> |
177 | | #endif |
178 | | |
179 | | #ifdef HAVE_ARPA_INET_H |
180 | | #include <arpa/inet.h> |
181 | | #endif |
182 | | |
183 | | #ifdef DNSSEC_LOCAL_VALIDATION |
184 | | #if 1 /*HAVE_ARPA_NAMESER_H*/ |
185 | | #include <arpa/nameser.h> |
186 | | #endif |
187 | | #include <validator/validator.h> |
188 | | /* NetSNMP and DNSSEC-Tools both define FREE. We'll not use either here. */ |
189 | | #undef FREE |
190 | | #endif |
191 | | |
192 | | #include <net-snmp/types.h> |
193 | | #include <net-snmp/output_api.h> |
194 | | #include <net-snmp/utilities.h> |
195 | | #include <net-snmp/library/system.h> /* for "internal" definitions */ |
196 | | |
197 | | #include <net-snmp/library/snmp_api.h> |
198 | | #include <net-snmp/library/read_config.h> /* for get_temp_file_pattern() */ |
199 | | |
200 | | #include "inet_ntop.h" |
201 | | |
202 | | /* NetSNMP and DNSSEC-Tools both define FREE. We'll not use either here. */ |
203 | | #undef FREE |
204 | | |
205 | | netsnmp_feature_child_of(system_all, libnetsnmp); |
206 | | |
207 | | netsnmp_feature_child_of(user_information, system_all); |
208 | | netsnmp_feature_child_of(calculate_sectime_diff, system_all); |
209 | | |
210 | | #ifndef IFF_LOOPBACK |
211 | | # define IFF_LOOPBACK 0 |
212 | | #endif |
213 | | |
214 | | #ifdef INADDR_LOOPBACK |
215 | 0 | # define LOOPBACK INADDR_LOOPBACK |
216 | | #else |
217 | | # define LOOPBACK 0x7f000001 |
218 | | #endif |
219 | | |
220 | | #ifndef EAI_FAIL |
221 | | # define EAI_FAIL -4 /* Non-recoverable failure in name res. */ |
222 | | #endif |
223 | | |
224 | | #if defined(HAVE_FORK) |
225 | | static void |
226 | | _daemon_prep(int stderr_log) |
227 | 0 | { |
228 | 0 | int fd; |
229 | | |
230 | | /* Avoid keeping any directory in use. */ |
231 | 0 | NETSNMP_IGNORE_RESULT(chdir("/")); |
232 | |
|
233 | 0 | if (stderr_log) |
234 | 0 | return; |
235 | | |
236 | 0 | fd = open("/dev/null", O_RDWR); |
237 | | |
238 | | /* |
239 | | * Close inherited file descriptors to avoid |
240 | | * keeping unnecessary references. |
241 | | */ |
242 | 0 | close(STDIN_FILENO); |
243 | 0 | close(STDOUT_FILENO); |
244 | 0 | close(STDERR_FILENO); |
245 | | |
246 | | /* |
247 | | * Redirect std{in,out,err} to /dev/null, just in case. |
248 | | */ |
249 | 0 | if (fd >= 0) { |
250 | 0 | dup2(fd, STDIN_FILENO); |
251 | 0 | dup2(fd, STDOUT_FILENO); |
252 | 0 | dup2(fd, STDERR_FILENO); |
253 | 0 | close(fd); |
254 | 0 | } |
255 | 0 | } |
256 | | #endif |
257 | | |
258 | | /** |
259 | | * fork current process into the background. |
260 | | * |
261 | | * This function forks a process into the background, in order to |
262 | | * become a daemon process. It does a few things along the way: |
263 | | * |
264 | | * - becoming a process/session group leader, and forking a second time so |
265 | | * that process/session group leader can exit. |
266 | | * |
267 | | * - changing the working directory to / |
268 | | * |
269 | | * - closing stdin, stdout and stderr (unless stderr_log is set) and |
270 | | * redirecting them to /dev/null |
271 | | * |
272 | | * @param quit_immediately : indicates if the parent process should |
273 | | * exit after a successful fork. |
274 | | * @param stderr_log : indicates if stderr is being used for |
275 | | * logging and shouldn't be closed |
276 | | * @returns -1 : fork error |
277 | | * 0 : child process returning |
278 | | * >0 : parent process returning. returned value is the child PID. |
279 | | */ |
280 | | int |
281 | | netsnmp_daemonize(int quit_immediately, int stderr_log) |
282 | 0 | { |
283 | 0 | int i = 0; |
284 | 0 | DEBUGMSGT(("daemonize","deamonizing...\n")); |
285 | 0 | #ifdef HAVE_FORK |
286 | | #ifdef HAVE__NSGETEXECUTABLEPATH |
287 | | char path [PATH_MAX] = ""; |
288 | | uint32_t size = sizeof (path); |
289 | | |
290 | | /* |
291 | | * if we are already launched in a "daemonized state", just |
292 | | * close & redirect the file descriptors |
293 | | */ |
294 | | if(getppid() <= 2) { |
295 | | _daemon_prep(stderr_log); |
296 | | return 0; |
297 | | } |
298 | | |
299 | | if (_NSGetExecutablePath (path, &size)) |
300 | | return -1; |
301 | | #endif |
302 | | /* |
303 | | * Fork to return control to the invoking process and to |
304 | | * guarantee that we aren't a process group leader. |
305 | | */ |
306 | | #ifdef HAVE_FORKALL |
307 | | i = forkall(); |
308 | | #else |
309 | 0 | i = fork(); |
310 | 0 | #endif |
311 | 0 | if (i != 0) { |
312 | | /* Parent. */ |
313 | 0 | DEBUGMSGT(("daemonize","first fork returned %d.\n", i)); |
314 | 0 | if(i == -1) { |
315 | 0 | snmp_log(LOG_ERR,"first fork failed (errno %d) in " |
316 | 0 | "netsnmp_daemonize()\n", errno); |
317 | 0 | return -1; |
318 | 0 | } |
319 | 0 | if (quit_immediately) { |
320 | 0 | DEBUGMSGT(("daemonize","parent exiting\n")); |
321 | 0 | exit(0); |
322 | 0 | } |
323 | 0 | } else { |
324 | | /* Child. */ |
325 | 0 | #ifdef HAVE_SETSID |
326 | | /* Become a process/session group leader. */ |
327 | 0 | setsid(); |
328 | 0 | #endif |
329 | | /* |
330 | | * Fork to let the process/session group leader exit. |
331 | | */ |
332 | | #ifdef HAVE_FORKALL |
333 | | i = forkall(); |
334 | | #else |
335 | 0 | i = fork(); |
336 | 0 | #endif |
337 | 0 | if (i != 0) { |
338 | 0 | DEBUGMSGT(("daemonize","second fork returned %d.\n", i)); |
339 | 0 | if(i == -1) { |
340 | 0 | snmp_log(LOG_ERR,"second fork failed (errno %d) in " |
341 | 0 | "netsnmp_daemonize()\n", errno); |
342 | 0 | } |
343 | | /* Parent. */ |
344 | 0 | exit(0); |
345 | 0 | } |
346 | 0 | #ifndef WIN32 |
347 | 0 | else { |
348 | | /* Child. */ |
349 | | |
350 | 0 | DEBUGMSGT(("daemonize","child continuing\n")); |
351 | |
|
352 | 0 | #if !defined(HAVE__NSGETARGV) |
353 | 0 | _daemon_prep(stderr_log); |
354 | | #else |
355 | | /* |
356 | | * Some darwin calls (using mach ports) don't work after |
357 | | * a fork. So, now that we've forked, we re-exec ourself |
358 | | * to ensure that the child's mach ports are all set up correctly, |
359 | | * the getppid call above will prevent the exec child from |
360 | | * forking... |
361 | | */ |
362 | | char * const *argv = *_NSGetArgv (); |
363 | | DEBUGMSGT(("daemonize","re-execing forked child\n")); |
364 | | execv (path, argv); |
365 | | snmp_log(LOG_ERR,"Forked child unable to re-exec - %s.\n", strerror (errno)); |
366 | | exit (0); |
367 | | #endif |
368 | 0 | } |
369 | 0 | #endif /* !WIN32 */ |
370 | 0 | } |
371 | 0 | #endif /* HAVE_FORK */ |
372 | 0 | return i; |
373 | 0 | } |
374 | | |
375 | | /* |
376 | | * ********************************************* |
377 | | */ |
378 | | #ifdef WIN32 |
379 | | in_addr_t |
380 | | get_myaddr(void) |
381 | | { |
382 | | char local_host[130]; |
383 | | int result; |
384 | | LPHOSTENT lpstHostent; |
385 | | SOCKADDR_IN in_addr, remote_in_addr; |
386 | | SOCKET hSock; |
387 | | int nAddrSize = sizeof(SOCKADDR); |
388 | | |
389 | | in_addr.sin_addr.s_addr = INADDR_ANY; |
390 | | |
391 | | result = gethostname(local_host, sizeof(local_host)); |
392 | | if (result == 0) { |
393 | | lpstHostent = gethostbyname((LPSTR) local_host); |
394 | | if (lpstHostent) { |
395 | | in_addr.sin_addr.s_addr = |
396 | | *((u_long FAR *) (lpstHostent->h_addr)); |
397 | | return ((in_addr_t) in_addr.sin_addr.s_addr); |
398 | | } |
399 | | } |
400 | | |
401 | | /* |
402 | | * if we are here, than we don't have host addr |
403 | | */ |
404 | | hSock = socket(AF_INET, SOCK_DGRAM, 0); |
405 | | if (hSock != INVALID_SOCKET) { |
406 | | /* |
407 | | * connect to any port and address |
408 | | */ |
409 | | remote_in_addr.sin_family = AF_INET; |
410 | | remote_in_addr.sin_port = htons(IPPORT_ECHO); |
411 | | remote_in_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); |
412 | | result = |
413 | | connect(hSock, (LPSOCKADDR) & remote_in_addr, |
414 | | sizeof(SOCKADDR)); |
415 | | if (result != SOCKET_ERROR) { |
416 | | /* |
417 | | * get local ip address |
418 | | */ |
419 | | getsockname(hSock, (LPSOCKADDR) & in_addr, |
420 | | (int FAR *) &nAddrSize); |
421 | | } |
422 | | closesocket(hSock); |
423 | | } |
424 | | return ((in_addr_t) in_addr.sin_addr.s_addr); |
425 | | } |
426 | | |
427 | | long |
428 | | get_uptime(void) |
429 | | { |
430 | | long return_value = 0; |
431 | | DWORD buffersize = (sizeof(PERF_DATA_BLOCK) + |
432 | | sizeof(PERF_OBJECT_TYPE)), |
433 | | type = REG_EXPAND_SZ; |
434 | | PPERF_DATA_BLOCK perfdata = NULL; |
435 | | |
436 | | /* |
437 | | * min requirement is one PERF_DATA_BLOCK plus one PERF_OBJECT_TYPE |
438 | | */ |
439 | | perfdata = (PPERF_DATA_BLOCK) malloc(buffersize); |
440 | | if (!perfdata) |
441 | | return 0; |
442 | | |
443 | | memset(perfdata, 0, buffersize); |
444 | | |
445 | | RegQueryValueEx(HKEY_PERFORMANCE_DATA, |
446 | | "Global", NULL, &type, (LPBYTE) perfdata, &buffersize); |
447 | | |
448 | | /* |
449 | | * we can not rely on the return value since there is always more so |
450 | | * we check the signature |
451 | | */ |
452 | | |
453 | | if (wcsncmp(perfdata->Signature, L"PERF", 4) == 0) { |
454 | | /* |
455 | | * signature ok, and all we need is in the in the PERF_DATA_BLOCK |
456 | | */ |
457 | | return_value = (long) ((perfdata->PerfTime100nSec.QuadPart / |
458 | | (LONGLONG) 100000)); |
459 | | } else |
460 | | return_value = GetTickCount() / 10; |
461 | | |
462 | | RegCloseKey(HKEY_PERFORMANCE_DATA); |
463 | | free(perfdata); |
464 | | |
465 | | return return_value; |
466 | | } |
467 | | |
468 | | char * |
469 | | winsock_startup(void) |
470 | | { |
471 | | WORD VersionRequested; |
472 | | WSADATA stWSAData; |
473 | | int i; |
474 | | static char errmsg[100]; |
475 | | |
476 | | /* winsock 1: use MAKEWORD(1,1) */ |
477 | | /* winsock 2: use MAKEWORD(2,2) */ |
478 | | |
479 | | VersionRequested = MAKEWORD(2,2); |
480 | | i = WSAStartup(VersionRequested, &stWSAData); |
481 | | if (i != 0) { |
482 | | if (i == WSAVERNOTSUPPORTED) |
483 | | sprintf(errmsg, |
484 | | "Unable to init. socket lib, does not support 1.1"); |
485 | | else { |
486 | | sprintf(errmsg, "Socket Startup error %d", i); |
487 | | } |
488 | | return (errmsg); |
489 | | } |
490 | | return (NULL); |
491 | | } |
492 | | |
493 | | void |
494 | | winsock_cleanup(void) |
495 | | { |
496 | | WSACleanup(); |
497 | | } |
498 | | |
499 | | #else /* ! WIN32 */ |
500 | | /*******************************************************************/ |
501 | | |
502 | | /* |
503 | | * XXX What if we have multiple addresses? Or no addresses for that matter? |
504 | | * XXX Could it be computed once then cached? Probably not worth it (not |
505 | | * used very often). |
506 | | */ |
507 | | in_addr_t |
508 | | get_myaddr(void) |
509 | 0 | { |
510 | 0 | int sd, i, lastlen = 0; |
511 | 0 | struct ifconf ifc; |
512 | 0 | struct ifreq *ifrp = NULL; |
513 | 0 | in_addr_t addr; |
514 | 0 | char *buf = NULL; |
515 | |
|
516 | 0 | if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
517 | 0 | return 0; |
518 | 0 | } |
519 | | |
520 | | /* |
521 | | * Cope with lots of interfaces and brokenness of ioctl SIOCGIFCONF on |
522 | | * some platforms; see W. R. Stevens, ``Unix Network Programming Volume |
523 | | * I'', p.435. |
524 | | */ |
525 | | |
526 | 0 | for (i = 8;; i += 8) { |
527 | 0 | buf = calloc(i, sizeof(struct ifreq)); |
528 | 0 | if (buf == NULL) { |
529 | 0 | close(sd); |
530 | 0 | return 0; |
531 | 0 | } |
532 | 0 | ifc.ifc_len = i * sizeof(struct ifreq); |
533 | 0 | ifc.ifc_buf = (caddr_t) buf; |
534 | |
|
535 | 0 | if (ioctl(sd, SIOCGIFCONF, (char *) &ifc) < 0) { |
536 | 0 | if (errno != EINVAL || lastlen != 0) { |
537 | | /* |
538 | | * Something has gone genuinely wrong. |
539 | | */ |
540 | 0 | free(buf); |
541 | 0 | close(sd); |
542 | 0 | return 0; |
543 | 0 | } |
544 | | /* |
545 | | * Otherwise, it could just be that the buffer is too small. |
546 | | */ |
547 | 0 | } else { |
548 | 0 | if (ifc.ifc_len == lastlen) { |
549 | | /* |
550 | | * The length is the same as the last time; we're done. |
551 | | */ |
552 | 0 | break; |
553 | 0 | } |
554 | 0 | lastlen = ifc.ifc_len; |
555 | 0 | } |
556 | 0 | free(buf); |
557 | 0 | } |
558 | | |
559 | 0 | for (ifrp = ifc.ifc_req; |
560 | 0 | (char *)ifrp < (char *)ifc.ifc_req + ifc.ifc_len; |
561 | | #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN |
562 | | ifrp = (struct ifreq *)(((char *) ifrp) + |
563 | | sizeof(ifrp->ifr_name) + |
564 | | ifrp->ifr_addr.sa_len) |
565 | | #else |
566 | 0 | ifrp++ |
567 | 0 | #endif |
568 | 0 | ) { |
569 | 0 | if (ifrp->ifr_addr.sa_family != AF_INET) { |
570 | 0 | continue; |
571 | 0 | } |
572 | 0 | addr = ((struct sockaddr_in *) &(ifrp->ifr_addr))->sin_addr.s_addr; |
573 | |
|
574 | 0 | if (ioctl(sd, SIOCGIFFLAGS, (char *) ifrp) < 0) { |
575 | 0 | continue; |
576 | 0 | } |
577 | 0 | if ((ifrp->ifr_flags & IFF_UP) |
578 | 0 | #ifdef IFF_RUNNING |
579 | 0 | && (ifrp->ifr_flags & IFF_RUNNING) |
580 | 0 | #endif /* IFF_RUNNING */ |
581 | 0 | && !(ifrp->ifr_flags & IFF_LOOPBACK) |
582 | 0 | && addr != LOOPBACK) { |
583 | | /* |
584 | | * I *really* don't understand why this is necessary. Perhaps for |
585 | | * some broken platform? Leave it for now. JBPN |
586 | | */ |
587 | 0 | #ifdef SYS_IOCTL_H_HAS_SIOCGIFADDR |
588 | 0 | if (ioctl(sd, SIOCGIFADDR, (char *) ifrp) < 0) { |
589 | 0 | continue; |
590 | 0 | } |
591 | 0 | addr = |
592 | 0 | ((struct sockaddr_in *) &(ifrp->ifr_addr))->sin_addr. |
593 | 0 | s_addr; |
594 | 0 | #endif |
595 | 0 | free(buf); |
596 | 0 | close(sd); |
597 | 0 | return addr; |
598 | 0 | } |
599 | 0 | } |
600 | 0 | free(buf); |
601 | 0 | close(sd); |
602 | 0 | return 0; |
603 | 0 | } |
604 | | |
605 | | |
606 | | #if !defined(solaris2) && !defined(linux) && !defined(cygwin) |
607 | | /* |
608 | | * Returns boottime in centiseconds(!). |
609 | | * Caches this for future use. |
610 | | */ |
611 | | long |
612 | | get_boottime(void) |
613 | | { |
614 | | static long boottime_csecs = 0; |
615 | | #if defined(hpux10) || defined(hpux11) |
616 | | struct pst_static pst_buf; |
617 | | #else |
618 | | struct timeval boottime; |
619 | | #ifdef NETSNMP_CAN_USE_SYSCTL |
620 | | int mib[2]; |
621 | | size_t len; |
622 | | #elif defined(NETSNMP_CAN_USE_NLIST) |
623 | | int kmem; |
624 | | #if !defined(hpux) |
625 | | static char boottime_name[] = "_boottime"; |
626 | | #else |
627 | | static char boottime_name[] = "boottime"; |
628 | | #endif |
629 | | static char empty_name[] = ""; |
630 | | struct nlist nl[2]; |
631 | | |
632 | | memset(nl, 0, sizeof(nl)); |
633 | | nl[0].n_name = boottime_name; |
634 | | nl[1].n_name = empty_name; |
635 | | #endif /* NETSNMP_CAN_USE_SYSCTL */ |
636 | | #endif /* hpux10 || hpux 11 */ |
637 | | |
638 | | |
639 | | if (boottime_csecs != 0) |
640 | | return (boottime_csecs); |
641 | | |
642 | | #if defined(hpux10) || defined(hpux11) |
643 | | pstat_getstatic(&pst_buf, sizeof(struct pst_static), 1, 0); |
644 | | boottime_csecs = pst_buf.boot_time * 100; |
645 | | #elif defined(NETSNMP_CAN_USE_SYSCTL) |
646 | | mib[0] = CTL_KERN; |
647 | | mib[1] = KERN_BOOTTIME; |
648 | | |
649 | | len = sizeof(boottime); |
650 | | |
651 | | sysctl(mib, 2, &boottime, &len, NULL, 0); |
652 | | boottime_csecs = (boottime.tv_sec * 100) + (boottime.tv_usec / 10000); |
653 | | #elif defined(NETSNMP_CAN_USE_NLIST) |
654 | | if ((kmem = open("/dev/kmem", 0)) < 0) |
655 | | return 0; |
656 | | nlist(KERNEL_LOC, nl); |
657 | | if (nl[0].n_type == 0) { |
658 | | close(kmem); |
659 | | return 0; |
660 | | } |
661 | | |
662 | | lseek(kmem, (long) nl[0].n_value, L_SET); |
663 | | read(kmem, &boottime, sizeof(boottime)); |
664 | | close(kmem); |
665 | | boottime_csecs = (boottime.tv_sec * 100) + (boottime.tv_usec / 10000); |
666 | | #else |
667 | | return 0; |
668 | | #endif /* hpux10 || hpux 11 */ |
669 | | |
670 | | return (boottime_csecs); |
671 | | } |
672 | | #endif |
673 | | |
674 | | /** |
675 | | * Returns the system uptime in centiseconds. |
676 | | * |
677 | | * @note The value returned by this function is not identical to sysUpTime |
678 | | * defined in RFC 1213. get_uptime() returns the system uptime while |
679 | | * sysUpTime represents the time that has elapsed since the most recent |
680 | | * restart of the network manager (snmpd). |
681 | | * |
682 | | * @see See also netsnmp_get_agent_uptime(). |
683 | | */ |
684 | | long |
685 | | get_uptime(void) |
686 | 0 | { |
687 | | #if defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7) |
688 | | static char lbolt_name[] = "lbolt"; |
689 | | struct nlist nl; |
690 | | int kmem; |
691 | | time_t lbolt; |
692 | | nl.n_name = lbolt_name; |
693 | | if(knlist(&nl, 1, sizeof(struct nlist)) != 0) return(0); |
694 | | if(nl.n_type == 0 || nl.n_value == 0) return(0); |
695 | | if((kmem = open("/dev/mem", 0)) < 0) return 0; |
696 | | lseek(kmem, (long) nl.n_value, L_SET); |
697 | | read(kmem, &lbolt, sizeof(lbolt)); |
698 | | close(kmem); |
699 | | return(lbolt); |
700 | | #elif defined(solaris2) |
701 | | kstat_ctl_t *ksc = kstat_open(); |
702 | | kstat_t *ks; |
703 | | kid_t kid; |
704 | | kstat_named_t *named; |
705 | | u_long lbolt = 0; |
706 | | |
707 | | if (ksc) { |
708 | | ks = kstat_lookup(ksc, NETSNMP_REMOVE_CONST(char *, "unix"), -1, |
709 | | NETSNMP_REMOVE_CONST(char *, "system_misc")); |
710 | | if (ks) { |
711 | | kid = kstat_read(ksc, ks, NULL); |
712 | | if (kid != -1) { |
713 | | named = kstat_data_lookup(ks, NETSNMP_REMOVE_CONST(char *, "lbolt")); |
714 | | if (named) { |
715 | | #ifdef KSTAT_DATA_UINT32 |
716 | | lbolt = named->value.ui32; |
717 | | #else |
718 | | lbolt = named->value.ul; |
719 | | #endif |
720 | | } |
721 | | } |
722 | | } |
723 | | kstat_close(ksc); |
724 | | } |
725 | | return lbolt; |
726 | | #elif defined(linux) || defined(cygwin) |
727 | 0 | FILE *in = fopen("/proc/uptime", "r"); |
728 | 0 | long uptim = 0, a, b; |
729 | 0 | if (in) { |
730 | 0 | if (2 == fscanf(in, "%ld.%ld", &a, &b)) |
731 | 0 | uptim = a * 100 + b; |
732 | 0 | fclose(in); |
733 | 0 | } |
734 | 0 | return uptim; |
735 | | #else |
736 | | struct timeval now; |
737 | | long boottime_csecs, nowtime_csecs; |
738 | | |
739 | | boottime_csecs = get_boottime(); |
740 | | if (boottime_csecs == 0) |
741 | | return 0; |
742 | | gettimeofday(&now, (struct timezone *) 0); |
743 | | nowtime_csecs = (now.tv_sec * 100) + (now.tv_usec / 10000); |
744 | | |
745 | | return (nowtime_csecs - boottime_csecs); |
746 | | #endif |
747 | 0 | } |
748 | | |
749 | | #endif /* ! WIN32 */ |
750 | | /*******************************************************************/ |
751 | | |
752 | | #ifdef DNSSEC_LOCAL_VALIDATION |
753 | | static val_context_t *_val_context = NULL; |
754 | | |
755 | | static val_context_t * |
756 | | netsnmp_validator_context(void) |
757 | | { |
758 | | if (NULL == _val_context) { |
759 | | int rc; |
760 | | char *apptype = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
761 | | NETSNMP_DS_LIB_APPTYPE); |
762 | | DEBUGMSGTL(("dns:sec:context", "creating dnssec context for %s\n", |
763 | | apptype)); |
764 | | rc = val_create_context(apptype, &_val_context); |
765 | | } |
766 | | |
767 | | return _val_context; |
768 | | } |
769 | | #endif /* DNSSEC_LOCAL_VALIDATION */ |
770 | | |
771 | | int |
772 | | netsnmp_gethostbyname_v4(const char* name, in_addr_t *addr_out) |
773 | 0 | { |
774 | 0 | #ifdef HAVE_GETADDRINFO |
775 | 0 | struct addrinfo *addrs = NULL; |
776 | 0 | struct addrinfo hint; |
777 | 0 | int err; |
778 | |
|
779 | 0 | memset(&hint, 0, sizeof hint); |
780 | 0 | hint.ai_flags = 0; |
781 | 0 | hint.ai_family = PF_INET; |
782 | 0 | hint.ai_socktype = SOCK_DGRAM; |
783 | 0 | hint.ai_protocol = 0; |
784 | |
|
785 | 0 | err = netsnmp_getaddrinfo(name, NULL, &hint, &addrs); |
786 | 0 | if (err != 0) { |
787 | 0 | return -1; |
788 | 0 | } |
789 | | |
790 | 0 | if (addrs != NULL) { |
791 | 0 | memcpy(addr_out, |
792 | 0 | &((struct sockaddr_in *) addrs->ai_addr)->sin_addr, |
793 | 0 | sizeof(in_addr_t)); |
794 | 0 | freeaddrinfo(addrs); |
795 | 0 | } else { |
796 | 0 | DEBUGMSGTL(("get_thisaddr", |
797 | 0 | "Failed to resolve IPv4 hostname\n")); |
798 | 0 | } |
799 | 0 | return 0; |
800 | |
|
801 | | #elif defined(HAVE_GETHOSTBYNAME) |
802 | | struct hostent *hp = NULL; |
803 | | |
804 | | hp = netsnmp_gethostbyname(name); |
805 | | if (hp == NULL) { |
806 | | DEBUGMSGTL(("get_thisaddr", |
807 | | "hostname (couldn't resolve)\n")); |
808 | | return -1; |
809 | | } else if (hp->h_addrtype != AF_INET) { |
810 | | DEBUGMSGTL(("get_thisaddr", |
811 | | "hostname (not AF_INET!)\n")); |
812 | | return -1; |
813 | | } else { |
814 | | DEBUGMSGTL(("get_thisaddr", |
815 | | "hostname (resolved okay)\n")); |
816 | | memcpy(addr_out, hp->h_addr, sizeof(in_addr_t)); |
817 | | } |
818 | | return 0; |
819 | | |
820 | | #elif defined(HAVE_GETIPNODEBYNAME) |
821 | | struct hostent *hp = NULL; |
822 | | int err; |
823 | | |
824 | | hp = getipnodebyname(peername, AF_INET, 0, &err); |
825 | | if (hp == NULL) { |
826 | | DEBUGMSGTL(("get_thisaddr", |
827 | | "hostname (couldn't resolve = %d)\n", err)); |
828 | | return -1; |
829 | | } |
830 | | DEBUGMSGTL(("get_thisaddr", |
831 | | "hostname (resolved okay)\n")); |
832 | | memcpy(addr_out, hp->h_addr, sizeof(in_addr_t)); |
833 | | return 0; |
834 | | |
835 | | #else /* HAVE_GETIPNODEBYNAME */ |
836 | | return -1; |
837 | | #endif |
838 | 0 | } |
839 | | |
840 | | int |
841 | | netsnmp_getaddrinfo(const char *name, const char *service, |
842 | | const struct addrinfo *hints, struct addrinfo **res) |
843 | 0 | { |
844 | 0 | #ifdef HAVE_GETADDRINFO |
845 | 0 | struct addrinfo *addrs = NULL; |
846 | 0 | struct addrinfo hint; |
847 | 0 | int err; |
848 | | #ifdef DNSSEC_LOCAL_VALIDATION |
849 | | val_status_t val_status; |
850 | | #endif |
851 | |
|
852 | 0 | DEBUGMSGTL(("dns:getaddrinfo", "looking up ")); |
853 | 0 | if (name) |
854 | 0 | DEBUGMSG(("dns:getaddrinfo", "\"%s\"", name)); |
855 | 0 | else |
856 | 0 | DEBUGMSG(("dns:getaddrinfo", "<NULL>")); |
857 | |
|
858 | 0 | if (service) |
859 | 0 | DEBUGMSG(("dns:getaddrinfo", ":\"%s\"", service)); |
860 | |
|
861 | 0 | if (hints) |
862 | 0 | DEBUGMSG(("dns:getaddrinfo", |
863 | 0 | " with hints ({.ai_flags = %#x, .ai_family = %s})", |
864 | 0 | hints->ai_flags, hints->ai_family == 0 ? "0" : |
865 | 0 | hints->ai_family == AF_INET ? "AF_INET" : |
866 | 0 | hints->ai_family == AF_INET6 ? "AF_INET6" : "?")); |
867 | 0 | else |
868 | 0 | DEBUGMSG(("dns:getaddrinfo", " with no hint")); |
869 | |
|
870 | 0 | DEBUGMSG(("dns:getaddrinfo", "\n")); |
871 | |
|
872 | 0 | if (NULL == hints) { |
873 | 0 | memset(&hint, 0, sizeof hint); |
874 | 0 | hint.ai_flags = 0; |
875 | 0 | hint.ai_family = PF_INET; |
876 | 0 | hint.ai_socktype = SOCK_DGRAM; |
877 | 0 | hint.ai_protocol = 0; |
878 | 0 | hints = &hint; |
879 | 0 | } else { |
880 | 0 | memcpy(&hint, hints, sizeof hint); |
881 | 0 | } |
882 | |
|
883 | 0 | #ifndef DNSSEC_LOCAL_VALIDATION |
884 | 0 | err = getaddrinfo(name, NULL, &hint, &addrs); |
885 | | #else /* DNSSEC_LOCAL_VALIDATION */ |
886 | | err = val_getaddrinfo(netsnmp_validator_context(), name, NULL, &hint, |
887 | | &addrs, &val_status); |
888 | | DEBUGMSGTL(("dns:sec:val", "err %d, val_status %d / %s; trusted: %d\n", |
889 | | err, val_status, p_val_status(val_status), |
890 | | val_istrusted(val_status))); |
891 | | if (! val_istrusted(val_status)) { |
892 | | int rc; |
893 | | if ((err != 0) && VAL_GETADDRINFO_HAS_STATUS(err)) { |
894 | | snmp_log(LOG_WARNING, |
895 | | "WARNING: UNTRUSTED error in DNS resolution for %s!\n", |
896 | | name); |
897 | | rc = EAI_FAIL; |
898 | | } else { |
899 | | snmp_log(LOG_WARNING, |
900 | | "The authenticity of DNS response is not trusted (%s)\n", |
901 | | p_val_status(val_status)); |
902 | | rc = EAI_NONAME; |
903 | | } |
904 | | /** continue anyways if DNSSEC_WARN_ONLY is set */ |
905 | | if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
906 | | NETSNMP_DS_LIB_DNSSEC_WARN_ONLY)) |
907 | | return rc; |
908 | | } |
909 | | |
910 | | |
911 | | #endif /* DNSSEC_LOCAL_VALIDATION */ |
912 | 0 | *res = addrs; |
913 | 0 | DEBUGIF("dns:getaddrinfo") { |
914 | 0 | if (err == 0 && addrs && addrs->ai_addr) { |
915 | 0 | const char *fam = "?"; |
916 | 0 | char dst[64] = "?"; |
917 | 0 | uint16_t port = 0; |
918 | |
|
919 | 0 | switch (addrs->ai_addr->sa_family) { |
920 | 0 | case AF_INET: { |
921 | 0 | struct sockaddr_in *sin = (struct sockaddr_in *)addrs->ai_addr; |
922 | |
|
923 | 0 | fam = "AF_INET"; |
924 | 0 | inet_ntop(AF_INET, &sin->sin_addr, dst, sizeof(dst)); |
925 | 0 | port = ntohs(sin->sin_port); |
926 | 0 | break; |
927 | 0 | } |
928 | 0 | case AF_INET6: { |
929 | 0 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) |
930 | 0 | addrs->ai_addr; |
931 | |
|
932 | 0 | fam = "AF_INET6"; |
933 | 0 | inet_ntop(AF_INET6, &sin6->sin6_addr, dst, sizeof(dst)); |
934 | 0 | port = ntohs(sin6->sin6_port); |
935 | 0 | break; |
936 | 0 | } |
937 | 0 | } |
938 | 0 | DEBUGMSGTL(("dns:getaddrinfo", "answer { %s, %s:%hu }\n", fam, dst, |
939 | 0 | port)); |
940 | 0 | } |
941 | 0 | } |
942 | 0 | return err; |
943 | | #else |
944 | | NETSNMP_LOGONCE((LOG_ERR, "getaddrinfo not available")); |
945 | | return EAI_FAIL; |
946 | | #endif /* getaddrinfo */ |
947 | 0 | } |
948 | | |
949 | | struct hostent * |
950 | | netsnmp_gethostbyname(const char *name) |
951 | 0 | { |
952 | 0 | #ifdef HAVE_GETHOSTBYNAME |
953 | | #ifdef DNSSEC_LOCAL_VALIDATION |
954 | | val_status_t val_status; |
955 | | #endif |
956 | 0 | struct hostent *hp = NULL; |
957 | |
|
958 | 0 | if (NULL == name) |
959 | 0 | return NULL; |
960 | | |
961 | 0 | DEBUGMSGTL(("dns:gethostbyname", "looking up %s\n", name)); |
962 | |
|
963 | | #ifdef DNSSEC_LOCAL_VALIDATION |
964 | | hp = val_gethostbyname(netsnmp_validator_context(), name, &val_status); |
965 | | DEBUGMSGTL(("dns:sec:val", "val_status %d / %s; trusted: %d\n", |
966 | | val_status, p_val_status(val_status), |
967 | | val_istrusted(val_status))); |
968 | | if (!val_istrusted(val_status)) { |
969 | | snmp_log(LOG_WARNING, |
970 | | "The authenticity of DNS response is not trusted (%s)\n", |
971 | | p_val_status(val_status)); |
972 | | /** continue anyways if DNSSEC_WARN_ONLY is set */ |
973 | | if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
974 | | NETSNMP_DS_LIB_DNSSEC_WARN_ONLY)) |
975 | | hp = NULL; |
976 | | } |
977 | | else if (val_does_not_exist(val_status) && hp) |
978 | | hp = NULL; |
979 | | #else |
980 | 0 | hp = gethostbyname(name); |
981 | 0 | #endif |
982 | 0 | if (hp == NULL) { |
983 | 0 | DEBUGMSGTL(("dns:gethostbyname", |
984 | 0 | "couldn't resolve %s\n", name)); |
985 | 0 | } else if (hp->h_addrtype != AF_INET |
986 | 0 | #ifdef AF_INET6 |
987 | 0 | && hp->h_addrtype != AF_INET6 |
988 | 0 | #endif |
989 | 0 | ) { |
990 | 0 | #ifdef AF_INET6 |
991 | 0 | DEBUGMSGTL(("dns:gethostbyname", |
992 | 0 | "warning: response for %s not AF_INET/AF_INET6!\n", name)); |
993 | | #else |
994 | | DEBUGMSGTL(("dns:gethostbyname", |
995 | | "warning: response for %s not AF_INET!\n", name)); |
996 | | #endif |
997 | 0 | } else { |
998 | 0 | DEBUGMSGTL(("dns:gethostbyname", |
999 | 0 | "%s resolved okay\n", name)); |
1000 | 0 | } |
1001 | 0 | return hp; |
1002 | | #else |
1003 | | NETSNMP_LOGONCE((LOG_ERR, "gethostbyname not available")); |
1004 | | return NULL; |
1005 | | #endif /* HAVE_GETHOSTBYNAME */ |
1006 | 0 | } |
1007 | | |
1008 | | /** |
1009 | | * Look up the host name via DNS. |
1010 | | * |
1011 | | * @param[in] addr Pointer to the address to resolve. This argument points e.g. |
1012 | | * to a struct in_addr for AF_INET or to a struct in6_addr for AF_INET6. |
1013 | | * @param[in] len Length in bytes of *addr. |
1014 | | * @param[in] type Address family, e.g. AF_INET or AF_INET6. |
1015 | | * |
1016 | | * @return Pointer to a hostent structure if address lookup succeeded or NULL |
1017 | | * if the lookup failed. |
1018 | | * |
1019 | | * @see See also the gethostbyaddr() man page. |
1020 | | */ |
1021 | | struct hostent * |
1022 | | netsnmp_gethostbyaddr(const void *addr, socklen_t len, int type) |
1023 | 0 | { |
1024 | 0 | #ifdef HAVE_GETHOSTBYADDR |
1025 | 0 | struct hostent *hp = NULL; |
1026 | 0 | char buf[64]; |
1027 | |
|
1028 | 0 | DEBUGMSGTL(("dns:gethostbyaddr", "resolving %s\n", |
1029 | 0 | inet_ntop(type, addr, buf, sizeof(buf)))); |
1030 | |
|
1031 | | #ifdef DNSSEC_LOCAL_VALIDATION |
1032 | | val_status_t val_status; |
1033 | | hp = val_gethostbyaddr(netsnmp_validator_context(), addr, len, type, |
1034 | | &val_status); |
1035 | | DEBUGMSGTL(("dns:sec:val", "val_status %d / %s; trusted: %d\n", |
1036 | | val_status, p_val_status(val_status), |
1037 | | val_istrusted(val_status))); |
1038 | | if (!val_istrusted(val_status)) { |
1039 | | snmp_log(LOG_WARNING, |
1040 | | "The authenticity of DNS response is not trusted (%s)\n", |
1041 | | p_val_status(val_status)); |
1042 | | /** continue anyways if DNSSEC_WARN_ONLY is set */ |
1043 | | if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
1044 | | NETSNMP_DS_LIB_DNSSEC_WARN_ONLY)) |
1045 | | hp = NULL; |
1046 | | } |
1047 | | else if (val_does_not_exist(val_status) && hp) |
1048 | | hp = NULL; |
1049 | | #else |
1050 | 0 | hp = gethostbyaddr(addr, len, type); |
1051 | 0 | #endif |
1052 | 0 | if (hp == NULL) { |
1053 | 0 | DEBUGMSGTL(("dns:gethostbyaddr", "couldn't resolve addr\n")); |
1054 | 0 | } else if (hp->h_addrtype != AF_INET) { |
1055 | 0 | DEBUGMSGTL(("dns:gethostbyaddr", |
1056 | 0 | "warning: response for addr not AF_INET!\n")); |
1057 | 0 | } else { |
1058 | 0 | DEBUGMSGTL(("dns:gethostbyaddr", "addr resolved okay\n")); |
1059 | 0 | } |
1060 | 0 | return hp; |
1061 | | #else |
1062 | | NETSNMP_LOGONCE((LOG_ERR, "gethostbyaddr not available")); |
1063 | | return NULL; |
1064 | | #endif |
1065 | 0 | } |
1066 | | |
1067 | | /*******************************************************************/ |
1068 | | |
1069 | | #if !defined(HAVE_STRNCASECMP) && !defined(strncasecmp) |
1070 | | |
1071 | | /* |
1072 | | * test for NULL pointers before and NULL characters after |
1073 | | * * comparing possibly non-NULL strings. |
1074 | | * * WARNING: This function does NOT check for array overflow. |
1075 | | */ |
1076 | | int |
1077 | | strncasecmp(const char *s1, const char *s2, size_t nch) |
1078 | | { |
1079 | | size_t ii; |
1080 | | int res = -1; |
1081 | | |
1082 | | if (!s1) { |
1083 | | if (!s2) |
1084 | | return 0; |
1085 | | return (-1); |
1086 | | } |
1087 | | if (!s2) |
1088 | | return (1); |
1089 | | |
1090 | | for (ii = 0; (ii < nch) && *s1 && *s2; ii++, s1++, s2++) { |
1091 | | res = (int) (tolower(*s1) - tolower(*s2)); |
1092 | | if (res != 0) |
1093 | | break; |
1094 | | } |
1095 | | |
1096 | | if (ii == nch) { |
1097 | | s1--; |
1098 | | s2--; |
1099 | | } |
1100 | | |
1101 | | if (!*s1) { |
1102 | | if (!*s2) |
1103 | | return 0; |
1104 | | return (-1); |
1105 | | } |
1106 | | if (!*s2) |
1107 | | return (1); |
1108 | | |
1109 | | return (res); |
1110 | | } |
1111 | | |
1112 | | int |
1113 | | strcasecmp(const char *s1, const char *s2) |
1114 | | { |
1115 | | return strncasecmp(s1, s2, 1000000); |
1116 | | } |
1117 | | |
1118 | | #endif /* !defined(HAVE_STRNCASECMP) && !defined(strncasecmp) */ |
1119 | | |
1120 | | |
1121 | | #ifndef HAVE_STRDUP |
1122 | | char * |
1123 | | strdup(const char *src) |
1124 | | { |
1125 | | int len; |
1126 | | char *dst; |
1127 | | |
1128 | | len = strlen(src) + 1; |
1129 | | if ((dst = (char *) malloc(len)) == NULL) |
1130 | | return (NULL); |
1131 | | strcpy(dst, src); |
1132 | | return (dst); |
1133 | | } |
1134 | | #endif /* HAVE_STRDUP */ |
1135 | | |
1136 | | #ifndef HAVE_SETENV |
1137 | | int |
1138 | | setenv(const char *name, const char *value, int overwrite) |
1139 | | { |
1140 | | char *cp; |
1141 | | int ret; |
1142 | | |
1143 | | if (overwrite == 0) { |
1144 | | if (getenv(name)) |
1145 | | return 0; |
1146 | | } |
1147 | | if (asprintf(&cp, "%s=%s", name, value) < 0) |
1148 | | return -1; |
1149 | | ret = putenv(cp); |
1150 | | #ifdef WIN32 |
1151 | | free(cp); |
1152 | | #endif |
1153 | | return ret; |
1154 | | } |
1155 | | #endif /* HAVE_SETENV */ |
1156 | | |
1157 | | netsnmp_feature_child_of(calculate_time_diff, netsnmp_unused); |
1158 | | #ifndef NETSNMP_FEATURE_REMOVE_CALCULATE_TIME_DIFF |
1159 | | /** |
1160 | | * Compute (*now - *then) in centiseconds. |
1161 | | */ |
1162 | | int |
1163 | | calculate_time_diff(const struct timeval *now, const struct timeval *then) |
1164 | 0 | { |
1165 | 0 | struct timeval diff; |
1166 | |
|
1167 | 0 | NETSNMP_TIMERSUB(now, then, &diff); |
1168 | 0 | return (int)(diff.tv_sec * 100 + diff.tv_usec / 10000); |
1169 | 0 | } |
1170 | | #endif /* NETSNMP_FEATURE_REMOVE_CALCULATE_TIME_DIFF */ |
1171 | | |
1172 | | #ifndef NETSNMP_FEATURE_REMOVE_CALCULATE_SECTIME_DIFF |
1173 | | /** Compute rounded (*now - *then) in seconds. */ |
1174 | | u_int |
1175 | | calculate_sectime_diff(const struct timeval *now, const struct timeval *then) |
1176 | 5.97k | { |
1177 | 5.97k | struct timeval diff; |
1178 | | |
1179 | 5.97k | NETSNMP_TIMERSUB(now, then, &diff); |
1180 | 5.97k | return (u_int)(diff.tv_sec + (diff.tv_usec >= 500000L)); |
1181 | 5.97k | } |
1182 | | #endif /* NETSNMP_FEATURE_REMOVE_CALCULATE_SECTIME_DIFF */ |
1183 | | |
1184 | | #ifndef HAVE_STRCASESTR |
1185 | | /* |
1186 | | * only glibc2 has this. |
1187 | | */ |
1188 | | char * |
1189 | | strcasestr(const char *haystack, const char *needle) |
1190 | | { |
1191 | | const char *cp1 = haystack, *cp2 = needle; |
1192 | | const char *cx; |
1193 | | int tstch1, tstch2; |
1194 | | |
1195 | | /* |
1196 | | * printf("looking for '%s' in '%s'\n", needle, haystack); |
1197 | | */ |
1198 | | if (cp1 && cp2 && *cp1 && *cp2) |
1199 | | for (cp1 = haystack, cp2 = needle; *cp1;) { |
1200 | | cx = cp1; |
1201 | | cp2 = needle; |
1202 | | do { |
1203 | | /* |
1204 | | * printf("T'%c' ", *cp1); |
1205 | | */ |
1206 | | if (!*cp2) { /* found the needle */ |
1207 | | /* |
1208 | | * printf("\nfound '%s' in '%s'\n", needle, cx); |
1209 | | */ |
1210 | | return NETSNMP_REMOVE_CONST(char *, cx); |
1211 | | } |
1212 | | if (!*cp1) |
1213 | | break; |
1214 | | |
1215 | | tstch1 = toupper(*cp1); |
1216 | | tstch2 = toupper(*cp2); |
1217 | | if (tstch1 != tstch2) |
1218 | | break; |
1219 | | /* |
1220 | | * printf("M'%c' ", *cp1); |
1221 | | */ |
1222 | | cp1++; |
1223 | | cp2++; |
1224 | | } |
1225 | | while (1); |
1226 | | if (*cp1) |
1227 | | cp1++; |
1228 | | } |
1229 | | /* |
1230 | | * printf("\n"); |
1231 | | */ |
1232 | | if (cp1 && *cp1) |
1233 | | return NETSNMP_REMOVE_CONST(char *, cp1); |
1234 | | |
1235 | | return NULL; |
1236 | | } |
1237 | | #endif |
1238 | | |
1239 | | int |
1240 | | mkdirhier(const char *pathname, mode_t mode, int skiplast) |
1241 | 8.96k | { |
1242 | 8.96k | struct stat sbuf; |
1243 | 8.96k | char *ourcopy = strdup(pathname); |
1244 | 8.96k | char *entry; |
1245 | 8.96k | char *buf = NULL; |
1246 | 8.96k | char *st = NULL; |
1247 | 8.96k | int res; |
1248 | | |
1249 | 8.96k | res = SNMPERR_GENERR; |
1250 | 8.96k | if (!ourcopy) |
1251 | 0 | goto out; |
1252 | | |
1253 | 8.96k | buf = malloc(strlen(pathname) + 2); |
1254 | 8.96k | if (!buf) |
1255 | 0 | goto out; |
1256 | | |
1257 | | #if defined (WIN32) || defined (cygwin) |
1258 | | /* convert backslash to forward slash */ |
1259 | | for (entry = ourcopy; *entry; entry++) |
1260 | | if (*entry == '\\') |
1261 | | *entry = '/'; |
1262 | | #endif |
1263 | | |
1264 | 8.96k | entry = strtok_r(ourcopy, "/", &st); |
1265 | | |
1266 | 8.96k | buf[0] = '\0'; |
1267 | | |
1268 | | #if defined (WIN32) || defined (cygwin) |
1269 | | /* |
1270 | | * Check if first entry contains a drive-letter |
1271 | | * e.g "c:/path" |
1272 | | */ |
1273 | | if ((entry) && (':' == entry[1]) && |
1274 | | (('\0' == entry[2]) || ('/' == entry[2]))) { |
1275 | | strcat(buf, entry); |
1276 | | entry = strtok_r(NULL, "/", &st); |
1277 | | } |
1278 | | #endif |
1279 | | |
1280 | | /* |
1281 | | * check to see if filename is a directory |
1282 | | */ |
1283 | 26.9k | while (entry) { |
1284 | 26.9k | strcat(buf, "/"); |
1285 | 26.9k | strcat(buf, entry); |
1286 | 26.9k | entry = strtok_r(NULL, "/", &st); |
1287 | 26.9k | if (entry == NULL && skiplast) |
1288 | 8.96k | break; |
1289 | 17.9k | if (stat(buf, &sbuf) < 0) { |
1290 | | /* |
1291 | | * DNE, make it |
1292 | | */ |
1293 | | #ifdef WIN32 |
1294 | | if (CreateDirectory(buf, NULL) == 0) |
1295 | | #else |
1296 | 0 | if (mkdir(buf, mode) == -1) |
1297 | 0 | #endif |
1298 | 0 | goto out; |
1299 | 0 | else |
1300 | 0 | snmp_log(LOG_INFO, "Created directory: %s\n", buf); |
1301 | 17.9k | } else { |
1302 | | /* |
1303 | | * exists, is it a file? |
1304 | | */ |
1305 | 17.9k | if ((sbuf.st_mode & S_IFDIR) == 0) { |
1306 | | /* |
1307 | | * ack! can't make a directory on top of a file |
1308 | | */ |
1309 | 0 | goto out; |
1310 | 0 | } |
1311 | 17.9k | } |
1312 | 17.9k | } |
1313 | 8.96k | res = SNMPERR_SUCCESS; |
1314 | 8.96k | out: |
1315 | 8.96k | free(buf); |
1316 | 8.96k | free(ourcopy); |
1317 | 8.96k | return res; |
1318 | 8.96k | } |
1319 | | |
1320 | | /** |
1321 | | * netsnmp_mktemp creates a temporary file based on the |
1322 | | * configured tempFilePattern |
1323 | | * |
1324 | | * @return file descriptor |
1325 | | */ |
1326 | | const char * |
1327 | | netsnmp_mktemp(void) |
1328 | 0 | { |
1329 | 0 | #ifdef PATH_MAX |
1330 | 0 | static char name[PATH_MAX]; |
1331 | | #else |
1332 | | static char name[256]; |
1333 | | #endif |
1334 | 0 | int fd = -1; |
1335 | |
|
1336 | 0 | strlcpy(name, get_temp_file_pattern(), sizeof(name)); |
1337 | 0 | #ifdef HAVE_MKSTEMP |
1338 | 0 | { |
1339 | 0 | mode_t oldmask = umask(~(S_IRUSR | S_IWUSR)); |
1340 | 0 | netsnmp_assert(oldmask != (mode_t)(-1)); |
1341 | 0 | fd = mkstemp(name); |
1342 | 0 | umask(oldmask); |
1343 | 0 | } |
1344 | | #else |
1345 | | if (mktemp(name)) { |
1346 | | # ifndef WIN32 |
1347 | | fd = open(name, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); |
1348 | | # else |
1349 | | /* |
1350 | | * Win32 needs _S_IREAD | _S_IWRITE to set permissions on file |
1351 | | * after closing |
1352 | | */ |
1353 | | fd = _open(name, _O_CREAT | _O_EXCL | _O_WRONLY, _S_IREAD | _S_IWRITE); |
1354 | | # endif |
1355 | | } |
1356 | | #endif |
1357 | 0 | if (fd >= 0) { |
1358 | 0 | close(fd); |
1359 | 0 | DEBUGMSGTL(("netsnmp_mktemp", "temp file created: %s\n", |
1360 | 0 | name)); |
1361 | 0 | return name; |
1362 | 0 | } |
1363 | 0 | snmp_log(LOG_ERR, "netsnmp_mktemp: error creating file %s\n", |
1364 | 0 | name); |
1365 | 0 | return NULL; |
1366 | 0 | } |
1367 | | |
1368 | | /* |
1369 | | * This function was created to differentiate actions |
1370 | | * that are appropriate for Linux 2.4 kernels, but not later kernels. |
1371 | | * |
1372 | | * This function can be used to test kernels on any platform that supports uname(). |
1373 | | * |
1374 | | * If not running a platform that supports uname(), return -1. |
1375 | | * |
1376 | | * If ospname matches, and the release matches up through the prefix, |
1377 | | * return 0. |
1378 | | * If the release is ordered higher, return 1. |
1379 | | * Be aware that "ordered higher" is not a guarantee of correctness. |
1380 | | */ |
1381 | | int |
1382 | | netsnmp_os_prematch(const char *ospmname, |
1383 | | const char *ospmrelprefix) |
1384 | 2.98k | { |
1385 | 2.98k | #ifdef HAVE_SYS_UTSNAME_H |
1386 | 2.98k | static int printOSonce = 1; |
1387 | 2.98k | struct utsname utsbuf; |
1388 | 2.98k | if ( 0 > uname(&utsbuf)) |
1389 | 0 | return -1; |
1390 | | |
1391 | 2.98k | if (printOSonce) { |
1392 | 1 | printOSonce = 0; |
1393 | | /* show the four elements that the kernel can be sure of */ |
1394 | 1 | DEBUGMSGT(("daemonize","sysname '%s',\nrelease '%s',\nversion '%s',\nmachine '%s'\n", |
1395 | 1 | utsbuf.sysname, utsbuf.release, utsbuf.version, utsbuf.machine)); |
1396 | 1 | } |
1397 | 2.98k | if (0 != strcasecmp(utsbuf.sysname, ospmname)) return -1; |
1398 | | |
1399 | | /* Required to match only the leading characters */ |
1400 | 2.98k | return strncasecmp(utsbuf.release, ospmrelprefix, strlen(ospmrelprefix)); |
1401 | | |
1402 | | #else |
1403 | | |
1404 | | return -1; |
1405 | | |
1406 | | #endif /* HAVE_SYS_UTSNAME_H */ |
1407 | 2.98k | } |
1408 | | |
1409 | | /** |
1410 | | * netsnmp_os_kernel_width determines kernel width at runtime |
1411 | | * Currently implemented for IRIX, AIX and Tru64 Unix |
1412 | | * |
1413 | | * @return kernel width (usually 32 or 64) on success, -1 on error |
1414 | | */ |
1415 | | int |
1416 | | netsnmp_os_kernel_width(void) |
1417 | 0 | { |
1418 | | #ifdef irix6 |
1419 | | char buf[8]; |
1420 | | sysinfo(_MIPS_SI_OS_NAME, buf, 7); |
1421 | | if (strncmp("IRIX64", buf, 6) == 0) { |
1422 | | return 64; |
1423 | | } else if (strncmp("IRIX", buf, 4) == 0) { |
1424 | | return 32; |
1425 | | } else { |
1426 | | return -1; |
1427 | | } |
1428 | | #elif defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7) |
1429 | | return (__KERNEL_32() ? 32 : (__KERNEL_64() ? 64 : -1)); |
1430 | | #elif defined(osf4) || defined(osf5) || defined(__alpha) |
1431 | | return 64; /* Alpha is always 64bit */ |
1432 | | #else |
1433 | | /* kernel width detection not implemented */ |
1434 | 0 | return -1; |
1435 | 0 | #endif |
1436 | 0 | } |
1437 | | |
1438 | | netsnmp_feature_child_of(str_to_uid, user_information); |
1439 | | #ifndef NETSNMP_FEATURE_REMOVE_STR_TO_UID |
1440 | | /** |
1441 | | * Convert a user name or number into numeric form. |
1442 | | * |
1443 | | * @param[in] useroruid Either a Unix user name or the ASCII representation |
1444 | | * of a user number. |
1445 | | * |
1446 | | * @return Either a user number > 0 or 0 if useroruid is not a valid user |
1447 | | * name, not a valid user number or the name of the root user. |
1448 | | */ |
1449 | 0 | int netsnmp_str_to_uid(const char *useroruid) { |
1450 | 0 | int uid; |
1451 | 0 | #if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H) |
1452 | 0 | struct passwd *pwd; |
1453 | 0 | #endif |
1454 | |
|
1455 | 0 | uid = atoi(useroruid); |
1456 | |
|
1457 | 0 | if (uid == 0) { |
1458 | 0 | #if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H) |
1459 | 0 | pwd = getpwnam(useroruid); |
1460 | 0 | uid = pwd ? pwd->pw_uid : 0; |
1461 | 0 | endpwent(); |
1462 | 0 | #endif |
1463 | 0 | if (uid == 0) |
1464 | 0 | snmp_log(LOG_WARNING, "Can't identify user (%s).\n", useroruid); |
1465 | 0 | } |
1466 | 0 | return uid; |
1467 | | |
1468 | 0 | } |
1469 | | #endif /* NETSNMP_FEATURE_REMOVE_STR_TO_UID */ |
1470 | | |
1471 | | netsnmp_feature_child_of(str_to_gid, user_information); |
1472 | | #ifndef NETSNMP_FEATURE_REMOVE_STR_TO_GID |
1473 | | /** |
1474 | | * Convert a group name or number into numeric form. |
1475 | | * |
1476 | | * @param[in] grouporgid Either a Unix group name or the ASCII representation |
1477 | | * of a group number. |
1478 | | * |
1479 | | * @return Either a group number > 0 or 0 if grouporgid is not a valid group |
1480 | | * name, not a valid group number or the root group. |
1481 | | */ |
1482 | | int netsnmp_str_to_gid(const char *grouporgid) |
1483 | 0 | { |
1484 | 0 | int gid; |
1485 | |
|
1486 | 0 | gid = atoi(grouporgid); |
1487 | |
|
1488 | 0 | if (gid == 0) { |
1489 | 0 | #if defined(HAVE_GETGRNAM) && defined(HAVE_GRP_H) |
1490 | 0 | struct group *grp; |
1491 | |
|
1492 | 0 | grp = getgrnam(grouporgid); |
1493 | 0 | gid = grp ? grp->gr_gid : 0; |
1494 | 0 | endgrent(); |
1495 | 0 | #endif |
1496 | 0 | if (gid == 0) |
1497 | 0 | snmp_log(LOG_WARNING, "Can't identify group (%s).\n", grouporgid); |
1498 | 0 | } |
1499 | |
|
1500 | 0 | return gid; |
1501 | 0 | } |
1502 | | #endif /* NETSNMP_FEATURE_REMOVE_STR_TO_GID */ |