1 /*-
2  * SSLsplit - transparent SSL/TLS interception
3  * https://www.roe.ch/SSLsplit
4  *
5  * Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "sys.h"
30 
31 #include "log.h"
32 #include "defaults.h"
33 
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <sys/uio.h>
38 #include <sys/stat.h>
39 #include <sys/file.h>
40 #include <sys/un.h>
41 #include <sys/time.h>
42 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <netdb.h>
45 #include <fcntl.h>
46 #include <pwd.h>
47 #include <grp.h>
48 #include <fts.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <errno.h>
54 
55 #ifndef _SC_NPROCESSORS_ONLN
56 #include <sys/sysctl.h>
57 #endif /* !_SC_NPROCESSORS_ONLN */
58 
59 #if HAVE_DARWIN_LIBPROC
60 #include <libproc.h>
61 #endif
62 
63 #include <event2/util.h>
64 
65 /*
66  * Permanently drop from root privileges to an unprivileged user account.
67  * Sets the real, effective and stored user and group ID and the list of
68  * ancillary groups.  This is only safe if the effective user ID is 0.
69  * If username is unset and the effective uid != uid, drop privs to uid.
70  * This is to support setuid bit configurations.
71  * If groupname is set, it will be used instead of the user's default primary
72  * group.
73  * If jaildir is set, also chroot to jaildir after reading system files
74  * but before dropping privileges.
75  * Returns 0 on success, -1 on failure.
76  */
77 int
sys_privdrop(const char * username,const char * groupname,const char * jaildir)78 sys_privdrop(const char *username, const char *groupname, const char *jaildir)
79 {
80 	struct passwd *pw = NULL;
81 	struct group *gr = NULL;
82 	int ret = -1;
83 
84 	if (groupname) {
85 		errno = 0;
86 		if (!(gr = getgrnam(groupname))) {
87 			log_err_printf("Failed to getgrnam group '%s': %s\n",
88 			               groupname, strerror(errno));
89 			goto error;
90 		}
91 	}
92 
93 	if (username) {
94 		errno = 0;
95 		if (!(pw = getpwnam(username))) {
96 			log_err_printf("Failed to getpwnam user '%s': %s\n",
97 			               username, strerror(errno));
98 			goto error;
99 		}
100 
101 		if (gr != NULL) {
102 			pw->pw_gid = gr->gr_gid;
103 		}
104 
105 		if (initgroups(username, pw->pw_gid) == -1) {
106 			log_err_printf("Failed to initgroups user '%s': %s\n",
107 			               username, strerror(errno));
108 			goto error;
109 		}
110 	}
111 
112 	if (jaildir) {
113 		if (chroot(jaildir) == -1) {
114 			log_err_printf("Failed to chroot to '%s': %s\n",
115 			               jaildir, strerror(errno));
116 			goto error;
117 		}
118 		if (chdir("/") == -1) {
119 			log_err_printf("Failed to chdir to '/': %s\n",
120 			               strerror(errno));
121 			goto error;
122 		}
123 	}
124 
125 	if (username) {
126 		if (setgid(pw->pw_gid) == -1) {
127 			log_err_printf("Failed to setgid to %i: %s\n",
128 			               pw->pw_gid, strerror(errno));
129 			goto error;
130 		}
131 		if (setuid(pw->pw_uid) == -1) {
132 			log_err_printf("Failed to setuid to %i: %s\n",
133 			               pw->pw_uid, strerror(errno));
134 			goto error;
135 		}
136 	} else if (getuid() != geteuid()) {
137 		if (setuid(getuid()) == -1) {
138 			log_err_printf("Failed to setuid(getuid()): %s\n",
139 			               strerror(errno));
140 			goto error;
141 		}
142 	}
143 
144 	ret = 0;
145 error:
146 	if (pw) {
147 		endpwent();
148 	}
149 	if (gr) {
150 		endgrent();
151 	}
152 	return ret;
153 }
154 
155 /*
156  * If the user exists and on successful lookup, return 0 and if uid != NULL,
157  * write the uid of *username* to the value pointed to by uid.
158  * Return -1 on failure or if the user does not exist.
159  */
160 int
sys_uid(const char * username,uid_t * uid)161 sys_uid(const char *username, uid_t *uid)
162 {
163 	struct passwd *pw;
164 	int rv;
165 
166 	errno = 0;
167 	if (!(pw = getpwnam(username))) {
168 		if (errno != 0 && errno != ENOENT) {
169 			log_err_printf("Failed to load user '%s': %s (%i)\n",
170 			               username, strerror(errno), errno);
171 		}
172 		rv = -1;
173 	} else {
174 		if (uid)
175 			*uid = pw->pw_uid;
176 		rv = 0;
177 	}
178 	endpwent();
179 	return rv;
180 }
181 
182 /*
183  * Returns 1 if username can be loaded from user database, 0 otherwise.
184  */
185 int
sys_isuser(const char * username)186 sys_isuser(const char *username)
187 {
188 	return sys_uid(username, NULL) == 0;
189 }
190 
191 /*
192  * If the group exists and on successful lookup, return 0 and if gid != NULL,
193  * write the gid of *groupname* to the value pointed to by gid.
194  * Return -1 on failure or if the group does not exist.
195  */
196 int
sys_gid(const char * groupname,gid_t * gid)197 sys_gid(const char *groupname, gid_t *gid)
198 {
199 	struct group *gr;
200 	int rv;
201 
202 	errno = 0;
203 	if (!(gr = getgrnam(groupname))) {
204 		if (errno != 0 && errno != ENOENT) {
205 			log_err_printf("Failed to load group '%s': %s (%i)\n",
206 			               groupname, strerror(errno), errno);
207 		}
208 		rv = -1;
209 	} else {
210 		if (gid)
211 			*gid = gr->gr_gid;
212 		rv = 0;
213 	}
214 	endgrent();
215 	return rv;
216 }
217 
218 /*
219  * Returns 1 if groupname can be loaded from group database, 0 otherwise.
220  */
221 int
sys_isgroup(const char * groupname)222 sys_isgroup(const char *groupname)
223 {
224 	return sys_gid(groupname, NULL) == 0;
225 }
226 
227 /*
228  * Returns 1 if username is equivalent to the current effective UID.
229  * Returns 0 otherwise.
230  */
231 int
sys_isgeteuid(const char * username)232 sys_isgeteuid(const char *username)
233 {
234 	uid_t uid;
235 
236 	if (sys_uid(username, &uid) == -1)
237 		return 0;
238 	if (uid == geteuid())
239 		return 1;
240 	return 0;
241 }
242 
243 /*
244  * Open and lock process ID file fn.
245  * Returns open file descriptor on success or -1 on errors.
246  */
247 int
sys_pidf_open(const char * fn)248 sys_pidf_open(const char *fn)
249 {
250 	int fd;
251 
252 	if ((fd = open(fn, O_RDWR|O_CREAT, DFLT_PIDFMODE)) == -1) {
253 		log_err_printf("Failed to open '%s': %s\n", fn,
254 		               strerror(errno));
255 		return -1;
256 	}
257 	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
258 		log_err_printf("Failed to lock '%s': %s\n", fn,
259 		               strerror(errno));
260 		close(fd);
261 		return -1;
262 	}
263 
264 	return fd;
265 }
266 
267 /*
268  * Write process ID to open process ID file descriptor fd.
269  * Returns 0 on success, -1 on errors.
270  */
271 int
sys_pidf_write(int fd)272 sys_pidf_write(int fd)
273 {
274 	char pidbuf[4*sizeof(pid_t)];
275 	int rv;
276 	ssize_t n;
277 
278 	rv = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
279 	if (rv == -1 || rv >= (int)sizeof(pidbuf))
280 		return -1;
281 
282 	n = write(fd, pidbuf, strlen(pidbuf));
283 	if (n < (ssize_t)strlen(pidbuf))
284 		return -1;
285 
286 	rv = fsync(fd);
287 	if (rv == -1)
288 		return -1;
289 
290 	rv = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
291 	if (rv == -1)
292 		return -1;
293 
294 	return 0;
295 }
296 
297 /*
298  * Close and remove open process ID file before quitting.
299  */
300 void
sys_pidf_close(int fd,const char * fn)301 sys_pidf_close(int fd, const char *fn)
302 {
303 	unlink(fn);
304 	close(fd);
305 }
306 
307 /*
308  * Converts a local uid into a printable string representation.
309  * Returns an allocated buffer which must be freed by caller, or NULL on error.
310  */
311 char *
sys_user_str(uid_t uid)312 sys_user_str(uid_t uid)
313 {
314 	static int bufsize = 0;
315 
316 	if (!bufsize) {
317 		/* on some platforms this compiles, but does not succeed */
318 		if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
319 			bufsize = 64;
320 		}
321 	}
322 
323 	char *buf, *newbuf;
324 	struct passwd pwd, *result = NULL;
325 	int rv;
326 	char *name;
327 
328 	if (!(buf = malloc(bufsize)))
329 		return NULL;
330 
331 	do {
332 		rv = getpwuid_r(uid, &pwd, buf, bufsize, &result);
333 		if (rv == 0) {
334 			if (result) {
335 				name = strdup(pwd.pw_name);
336 				free(buf);
337 				return name;
338 			}
339 			free(buf);
340 
341 			/* no entry found; return the integer representation */
342 			if (asprintf(&name, "%llu", (long long) uid) < 0) {
343 				return NULL;
344 			}
345 			return name;
346 		}
347 		bufsize *= 2;
348 		if (!(newbuf = realloc(buf, bufsize))) {
349 			free(buf);
350 			return NULL;
351 		}
352 		buf = newbuf;
353 	} while (rv == ERANGE);
354 
355 	free(buf);
356 	log_err_printf("Failed to lookup uid: %s (%i)\n", strerror(rv), rv);
357 	return NULL;
358 }
359 
360 /*
361  * Converts a local gid into a printable string representation.
362  * Returns an allocated buffer which must be freed by caller, or NULL on error.
363  */
364 char *
sys_group_str(gid_t gid)365 sys_group_str(gid_t gid)
366 {
367 	static int bufsize = 0;
368 
369 	if (!bufsize) {
370 		/* on some platforms this compiles, but does not succeed */
371 		if ((bufsize = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
372 			bufsize = 64;
373 		}
374 	}
375 
376 	char *buf, *newbuf;
377 	struct group grp, *result = NULL;
378 	int rv;
379 	char *name;
380 
381 	if (!(buf = malloc(bufsize)))
382 		return NULL;
383 
384 	do {
385 		rv = getgrgid_r(gid, &grp, buf, bufsize, &result);
386 		if (rv == 0) {
387 			if (result) {
388 				name = strdup(grp.gr_name);
389 				free(buf);
390 				return name;
391 			}
392 			free(buf);
393 
394 			/* no entry found; return the integer representation */
395 			if (asprintf(&name, "%llu", (long long) gid) < 0) {
396 				return NULL;
397 			}
398 			return name;
399 		}
400 		bufsize *= 2;
401 		if (!(newbuf = realloc(buf, bufsize))) {
402 			free(buf);
403 			return NULL;
404 		}
405 		buf = newbuf;
406 	} while (rv == ERANGE);
407 
408 	free(buf);
409 	log_err_printf("Failed to lookup gid: %s (%i)\n", strerror(rv), rv);
410 	return NULL;
411 }
412 
413 /*
414  * Determine address family of addr
415  */
416 int
sys_get_af(const char * addr)417 sys_get_af(const char *addr)
418 {
419 	if (strstr(addr, ":"))
420 		return AF_INET6;
421 	else if (!strpbrk(addr, "abcdefghijklmnopqrstu"
422 							"vwxyzABCDEFGHIJKLMNOP"
423 							"QRSTUVWXYZ-"))
424 		return AF_INET;
425 	else
426 		return AF_UNSPEC;
427 }
428 
429 /*
430  * Parse an ascii host/IP and port tuple into a sockaddr_storage.
431  * On success, returns address family and fills in addr, addrlen.
432  * Returns -1 on error.
433  */
434 int
sys_sockaddr_parse(struct sockaddr_storage * addr,socklen_t * addrlen,char * naddr,char * nport,int af,int flags)435 sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
436                    char *naddr, char *nport, int af, int flags)
437 {
438 	struct evutil_addrinfo hints;
439 	struct evutil_addrinfo *ai;
440 	int rv;
441 
442 	memset(&hints, 0, sizeof(hints));
443 	hints.ai_family = af;
444 	hints.ai_socktype = SOCK_STREAM;
445 	hints.ai_protocol = IPPROTO_TCP;
446 	hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
447 	rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
448 	if (rv != 0) {
449 		log_err_printf("Cannot resolve address '%s' port '%s': %s\n",
450 		               naddr, nport, gai_strerror(rv));
451 		return -1;
452 	}
453 	memcpy(addr, ai->ai_addr, ai->ai_addrlen);
454 	*addrlen = ai->ai_addrlen;
455 	af = ai->ai_family;
456 	freeaddrinfo(ai);
457 	return af;
458 }
459 
460 /*
461  * Converts an IPv4/IPv6 sockaddr into printable string representations of the
462  * host and the service (port) part.  Writes allocated buffers to *host and
463  * *serv which must both be freed by the caller.  Neither *host nor *port are
464  * freed by this function before newly allocating.
465  * Returns 0 on success, -1 otherwise.  When -1 is returned, pointers in *host
466  * and *serv are invalid and must not be used nor freed by the caller.
467  */
468 int
sys_sockaddr_str(struct sockaddr * addr,socklen_t addrlen,char ** host,char ** serv)469 sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen,
470                  char **host, char **serv)
471 {
472 	char tmphost[INET6_ADDRSTRLEN];
473 	int rv;
474 	size_t hostsz;
475 
476 	*serv = malloc(6); /* max decimal digits of short plus terminator */
477 	if (!*serv) {
478 		log_err_printf("Cannot allocate memory\n");
479 		return -1;
480 	}
481 	rv = getnameinfo(addr, addrlen,
482 	                 tmphost, sizeof(tmphost),
483 	                 *serv, 6,
484 	                 NI_NUMERICHOST | NI_NUMERICSERV);
485 	if (rv != 0) {
486 		log_err_printf("Cannot get nameinfo for socket address: %s\n",
487 		               gai_strerror(rv));
488 		free(*serv);
489 		return -1;
490 	}
491 	hostsz = strlen(tmphost) + 1; /* including terminator */
492 	*host = malloc(hostsz);
493 	if (!*host) {
494 		log_err_printf("Cannot allocate memory\n");
495 		free(*serv);
496 		return -1;
497 	}
498 	memcpy(*host, tmphost, hostsz);
499 	return 0;
500 }
501 
502 /*
503  * Sanitizes a valid IPv4 or IPv6 address for use in a filename, i.e. removes
504  * characters that are invalid on NTFS and replaces them with more innocent
505  * characters.  The function assumes that the input is a valid IPv4 or IPv6
506  * address; it is not a generic filename sanitizer.
507  *
508  * Returns a copy of string s that must be freed by the caller.
509  *
510  * Invalid NTFS characters are < > : " / \ | ? * according to
511  * https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247.aspx
512  */
513 char *
sys_ip46str_sanitize(const char * s)514 sys_ip46str_sanitize(const char *s)
515 {
516 	char *copy, *p;
517 
518 	copy = strdup(s);
519 	if (!copy)
520 		return NULL;
521 	p = copy;
522 	while (*p) {
523 		switch (*p) {
524 		case ':':
525 		case '%':
526 			*p = '_';
527 			break;
528 		}
529 		p++;
530 	}
531 
532 	return copy;
533 }
534 
535 /*
536  * Returns the MTU of the interface with name *ifname* or 0 on errors.
537  */
538 size_t
sys_get_mtu(const char * ifname)539 sys_get_mtu(const char *ifname)
540 {
541 	struct ifreq ifr;
542 	size_t ifnamelen;
543 	int s;
544 
545 	ifnamelen = strlen(ifname);
546 	if (ifnamelen > sizeof(ifr.ifr_name) + 1)
547 		return 0;
548 	memcpy(ifr.ifr_name, ifname, ifnamelen);
549 	ifr.ifr_name[ifnamelen] = '\0';
550 
551 	s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
552 	if (s == -1)
553 		return 0;
554 	if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
555 		close(s);
556 		return 0;
557 	}
558 	close(s);
559 	return ifr.ifr_mtu;
560 }
561 
562 /*
563  * Returns 1 if path points to an existing directory node in the filesystem.
564  * Returns 0 if path is NULL, does not exist, or points to a file of some kind.
565  */
566 int
sys_isdir(const char * path)567 sys_isdir(const char *path)
568 {
569 	struct stat s;
570 
571 	if (stat(path, &s) == -1) {
572 		if (errno != ENOENT) {
573 			log_err_printf("Error stating file: %s (%i)\n",
574 			               strerror(errno), errno);
575 		}
576 		return 0;
577 	}
578 	if (s.st_mode & S_IFDIR)
579 		return 1;
580 	return 0;
581 }
582 
583 /*
584  * Create directory including parent directories with mode_t.
585  * Mode of existing parent directories is not changed.
586  * Returns 0 on success, -1 and sets errno on error.
587  */
588 int
sys_mkpath(const char * path,mode_t mode)589 sys_mkpath(const char *path, mode_t mode)
590 {
591 	char parent[strlen(path)+1];
592 	char *p;
593 
594 	memcpy(parent, path, sizeof(parent));
595 
596 	p = parent;
597 	do {
598 		/* skip leading '/' characters */
599 		while (*p == '/') p++;
600 		p = strchr(p, '/');
601 		if (p) {
602 			/* overwrite '/' to terminate the string at the next
603 			 * parent directory */
604 			*p = '\0';
605 		}
606 
607 		struct stat sbuf;
608 		if (stat(parent, &sbuf) == -1) {
609 			if (errno == ENOENT) {
610 				if (mkdir(parent, mode) != 0)
611 					return -1;
612 			} else {
613 				return -1;
614 			}
615 		} else if (!S_ISDIR(sbuf.st_mode)) {
616 			errno = ENOTDIR;
617 			return -1;
618 		}
619 
620 		if (p) {
621 			/* replace the overwritten slash */
622 			*p = '/';
623 			p++;
624 		}
625 	} while (p);
626 
627 	return 0;
628 }
629 
630 /*
631  * Return realpath(dirname(path)) + / + basename(path) in a newly allocated
632  * string.  Returns NULL on failure and sets errno to ENOENT if the directory
633  * part does not exist.
634  */
635 char *
sys_realdir(const char * path)636 sys_realdir(const char *path)
637 {
638 	char *sep, *udir, *rdir, *p;
639 	int rerrno, rv;
640 
641 	if (path[0] == '\0') {
642 		errno = EINVAL;
643 		return NULL;
644 	}
645 
646 	udir = strdup(path);
647 	if (!udir)
648 		return NULL;
649 
650 	sep = strrchr(udir, '/');
651 	if (!sep) {
652 		free(udir);
653 		rv = asprintf(&udir, "./%s", path);
654 		if (rv == -1)
655 			return NULL;
656 		sep = udir + 1;
657 	} else if (sep == udir) {
658 		return udir;
659 	}
660 	*sep = '\0';
661 	rdir = realpath(udir, NULL);
662 	if (!rdir) {
663 		rerrno = errno;
664 		free(udir);
665 		errno = rerrno;
666 		return NULL;
667 	}
668 	rv = asprintf(&p, "%s/%s", rdir, sep + 1);
669 	rerrno = errno;
670 	free(rdir);
671 	free(udir);
672 	errno = rerrno;
673 	if (rv == -1)
674 		return NULL;
675 	return p;
676 }
677 
678 /*
679  * Iterate over all files in a directory hierarchy, calling the callback
680  * cb for each file, passing the filename and arg as arguments.  Files and
681  * directories beginning with a dot are skipped, symlinks are followed.
682  */
683 int
sys_dir_eachfile(const char * dirname,sys_dir_eachfile_cb_t cb,void * arg)684 sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
685 {
686 	FTS *tree;
687 	FTSENT *node;
688 	char * paths[2];
689 	int rv = 0;
690 
691 	paths[1] = NULL;
692 	paths[0] = strdup(dirname);
693 	if (!paths[0])
694 		return -1;
695 
696 	tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, NULL);
697 	if (!tree) {
698 		log_err_printf("Cannot open directory '%s': %s\n",
699 		               dirname, strerror(errno));
700 		rv = -1;
701 		goto out1;
702 	}
703 
704 	while ((node = fts_read(tree))) {
705 		if (node->fts_level > 0 && node->fts_name[0] == '.')
706 			fts_set(tree, node, FTS_SKIP);
707 		else if (node->fts_info & FTS_F) {
708 			rv = cb(node->fts_path, arg);
709 			if (rv == -1)
710 				goto out2;
711 		}
712 	}
713 	if (errno) {
714 		log_err_printf("Error reading directory entry: %s\n",
715 		               strerror(errno));
716 		rv = -1;
717 		goto out2;
718 	}
719 
720 out2:
721 	fts_close(tree);
722 
723 out1:
724 	free(paths[0]);
725 	return rv;
726 }
727 
728 /*
729  * Portably get the number of CPU cores online in the system.
730  */
731 uint32_t
sys_get_cpu_cores(void)732 sys_get_cpu_cores(void)
733 {
734 #ifdef _SC_NPROCESSORS_ONLN
735 	return sysconf(_SC_NPROCESSORS_ONLN);
736 #else /* !_SC_NPROCESSORS_ONLN */
737 	int mib[2];
738 	uint32_t n;
739 	size_t len = sizeof(n);
740 
741 	mib[0] = CTL_HW;
742 	mib[1] = HW_AVAILCPU;
743 	sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
744 
745 	if (n < 1) {
746 		mib[1] = HW_NCPU;
747 		sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
748 		if (n < 1) {
749 			n = 1;
750 		}
751 	}
752 	return n;
753 #endif /* !_SC_NPROCESSORS_ONLN */
754 }
755 
756 /*
757  * Send a message and optional file descriptor on a connected AF_UNIX
758  * SOCKET_DGRAM socket s.  Returns the return value of sendmsg().
759  * If fd is -1, no file descriptor is passed.
760  */
761 ssize_t
sys_sendmsgfd(int sock,void * buf,size_t bufsz,int fd)762 sys_sendmsgfd(int sock, void *buf, size_t bufsz, int fd)
763 {
764 	struct iovec iov;
765 	struct msghdr msg;
766 	struct cmsghdr *cmsg;
767 	char cmsgbuf[CMSG_SPACE(sizeof(int))];
768 	ssize_t n;
769 
770 	iov.iov_base = buf;
771 	iov.iov_len = bufsz;
772 
773 	msg.msg_name = NULL;
774 	msg.msg_namelen = 0;
775 	msg.msg_iov = &iov;
776 	msg.msg_iovlen = 1;
777 	msg.msg_flags = 0;
778 
779 	if (fd != -1) {
780 		msg.msg_control = cmsgbuf;
781 		msg.msg_controllen = sizeof(cmsgbuf);
782 		memset(cmsgbuf, 0, sizeof(cmsgbuf));
783 
784 		cmsg = CMSG_FIRSTHDR(&msg);
785 		if (!cmsg)
786 			return -1;
787 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
788 		cmsg->cmsg_level = SOL_SOCKET;
789 		cmsg->cmsg_type = SCM_RIGHTS;
790 
791 		*((int *) CMSG_DATA(cmsg)) = fd;
792 	} else {
793 		msg.msg_control = NULL;
794 		msg.msg_controllen = 0;
795 	}
796 	do {
797 #ifdef MSG_NOSIGNAL
798 		n = sendmsg(sock, &msg, MSG_NOSIGNAL);
799 #else /* !MSG_NOSIGNAL */
800 		n = sendmsg(sock, &msg, 0);
801 #endif /* !MSG_NOSIGNAL */
802 	} while (n == -1 && errno == EINTR);
803 	return n;
804 }
805 
806 /*
807  * Receive a message and optional file descriptor on a connected AF_UNIX
808  * SOCKET_DGRAM socket s.  Returns the return value of recvmsg()/recv()
809  * and sets errno to EINVAL if the received message is malformed.
810  * If pfd is NULL, no file descriptor is received; if a file descriptor was
811  * part of the received message and pfd is NULL, then the kernel will close it.
812  */
813 ssize_t
sys_recvmsgfd(int sock,void * buf,size_t bufsz,int * pfd)814 sys_recvmsgfd(int sock, void *buf, size_t bufsz, int *pfd)
815 {
816 	ssize_t n;
817 
818 	if (pfd) {
819 		struct iovec iov;
820 		struct msghdr msg;
821 		struct cmsghdr *cmsg;
822 		unsigned char cmsgbuf[CMSG_SPACE(sizeof(int))];
823 
824 		iov.iov_base = buf;
825 		iov.iov_len = bufsz;
826 
827 		msg.msg_name = NULL;
828 		msg.msg_namelen = 0;
829 		msg.msg_iov = &iov;
830 		msg.msg_iovlen = 1;
831 		msg.msg_control = cmsgbuf;
832 		msg.msg_controllen = sizeof(cmsgbuf);
833 		do {
834 			n = recvmsg(sock, &msg, 0);
835 		} while (n == -1 && errno == EINTR);
836 		if (n <= 0)
837 			return n;
838 		cmsg = CMSG_FIRSTHDR(&msg);
839 		if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
840 			if (cmsg->cmsg_level != SOL_SOCKET) {
841 				errno = EINVAL;
842 				return -1;
843 			}
844 			if (cmsg->cmsg_type != SCM_RIGHTS) {
845 				errno = EINVAL;
846 				return -1;
847 			}
848 			*pfd = *((int *) CMSG_DATA(cmsg));
849 		} else {
850 			*pfd = -1;
851 		}
852 	} else {
853 		do {
854 			n = recv(sock, buf, bufsz, 0);
855 		} while (n == -1 && errno == EINTR);
856 	}
857 	return n;
858 }
859 
860 /*
861  * Format AF_UNIX socket address into printable string.
862  * Returns newly allocated string that must be freed by caller.
863  */
864 static char *
sys_afunix_str(struct sockaddr * addr,socklen_t addrlen)865 sys_afunix_str(struct sockaddr *addr, socklen_t addrlen)
866 {
867 	struct sockaddr_un *sun = (struct sockaddr_un *)addr;
868 	char *name;
869 	int rv;
870 
871 	if (addrlen == sizeof(sa_family_t)) {
872 		rv = asprintf(&name, "unnmd");
873 	} else if (sun->sun_path[0] == '\0') {
874 		/* abstract sockets is a Linux feature */
875 		rv = asprintf(&name, "abstr:%02x:%02x:%02x:%02x",
876 		                     sun->sun_path[1],
877 		                     sun->sun_path[2],
878 		                     sun->sun_path[3],
879 		                     sun->sun_path[4]);
880 	} else {
881 		rv = asprintf(&name, "pname:%s", sun->sun_path);
882 	}
883 	if (rv == -1)
884 		name = NULL;
885 	return name;
886 }
887 
888 /*
889  * Dump all open file descriptors to stdout - poor man's lsof/fstat/sockstat
890  */
891 void
sys_dump_fds(void)892 sys_dump_fds(void)
893 {
894 	int maxfd = 0;
895 
896 #ifdef F_MAXFD
897 	if (!maxfd && ((maxfd = fcntl(0, F_MAXFD)) == -1)) {
898 		fprintf(stderr, "fcntl(0, F_MAXFD) failed: %s (%i)\n",
899 		                strerror(errno), errno);
900 	}
901 #endif /* F_MAXFD */
902 #ifdef _SC_OPEN_MAX
903 	if (!maxfd && ((maxfd = sysconf(_SC_OPEN_MAX)) == -1)) {
904 		fprintf(stderr, "sysconf(_SC_OPEN_MAX) failed: %s (%i)\n",
905 		                strerror(errno), errno);
906 	}
907 #endif /* _SC_OPEN_MAX */
908 	if (!maxfd)
909 		maxfd = 65535;
910 
911 	for (int fd = 0; fd <= maxfd; fd++) {
912 		struct stat st;
913 
914 		if (fstat(fd, &st) == -1) {
915 			continue;
916 		}
917 
918 		printf("%5d:", fd);
919 		switch (st.st_mode & S_IFMT) {
920 		case S_IFBLK:  printf(" blkdev"); break;
921 		case S_IFCHR:  printf(" chrdev"); break;
922 		case S_IFDIR:  printf(" dir   "); break;
923 		case S_IFIFO:  printf(" fifo  "); break;
924 		case S_IFLNK:  printf(" lnkfil"); break;
925 		case S_IFREG:  printf(" regfil"); break;
926 		case S_IFSOCK: printf(" socket"); break;
927 		default:       printf(" unknwn"); break;
928 		}
929 
930 		if ((st.st_mode & S_IFMT) == S_IFSOCK) {
931 			int lrv, frv, arv;
932 			struct sockaddr_storage lss, fss;
933 			socklen_t lsslen = sizeof(lss);
934 			socklen_t fsslen = sizeof(fss);
935 			char *laddrstr, *faddrstr;
936 
937 			lrv = getsockname(fd, (struct sockaddr *)&lss, &lsslen);
938 			frv = getpeername(fd, (struct sockaddr *)&fss, &fsslen);
939 
940 			switch (lss.ss_family) {
941 			case AF_INET:
942 			case AF_INET6: {
943 				if (lrv == 0) {
944 					char *host, *port;
945 					if (sys_sockaddr_str(
946 					        (struct sockaddr *)&lss,
947 					        lsslen,
948 					        &host, &port) != 0) {
949 						laddrstr = strdup("?");
950 					} else {
951 						arv = asprintf(&laddrstr,
952 						               "[%s]:%s",
953 						               host, port);
954 						if (arv == -1)
955 							laddrstr = NULL;
956 						free(host);
957 						free(port);
958 					}
959 				} else {
960 					laddrstr = strdup("n/a");
961 				}
962 				if (frv == 0) {
963 					char *host, *port;
964 					if (sys_sockaddr_str(
965 					        (struct sockaddr *)&fss,
966 					        fsslen,
967 					        &host, &port) != 0) {
968 						faddrstr = strdup("?");
969 					} else {
970 						arv = asprintf(&faddrstr,
971 						               "[%s]:%s",
972 						               host, port);
973 						if (arv == -1)
974 							faddrstr = NULL;
975 						free(host);
976 						free(port);
977 					}
978 				} else {
979 					faddrstr = strdup("n/a");
980 				}
981 				printf(" %-6s %s -> %s",
982 				       lss.ss_family == AF_INET ? "in" : "in6",
983 				       laddrstr, faddrstr);
984 				free(laddrstr);
985 				free(faddrstr);
986 				break;
987 			}
988 			case AF_UNIX: {
989 				if (lrv == 0) {
990 					laddrstr = sys_afunix_str((struct sockaddr *)&lss, lsslen);
991 				} else {
992 					laddrstr = strdup("n/a");
993 				}
994 				if (frv == 0) {
995 					faddrstr = sys_afunix_str((struct sockaddr *)&fss, fsslen);
996 				} else {
997 					faddrstr = strdup("n/a");
998 				}
999 				printf(" unix   %s -> %s", laddrstr, faddrstr);
1000 				free(laddrstr);
1001 				free(faddrstr);
1002 				break;
1003 			}
1004 			case AF_UNSPEC: {
1005 				printf(" unspec");
1006 				break;
1007 			}
1008 			default:
1009 				printf(" (%i)", lss.ss_family);
1010 			}
1011 		}
1012 		printf("\n");
1013 	}
1014 }
1015 
1016 static int sys_rand_seeded = 0;
1017 
1018 static void
sys_rand_seed(void)1019 sys_rand_seed(void) {
1020 	struct timeval seed;
1021 
1022 	if (gettimeofday(&seed, NULL) == -1) {
1023 		srandom((unsigned)time(NULL));
1024 	} else {
1025 		srandom((unsigned)(seed.tv_sec ^ seed.tv_usec));
1026 	}
1027 	sys_rand_seeded = 1;
1028 }
1029 
1030 uint16_t
sys_rand16(void)1031 sys_rand16(void) {
1032 	if (unlikely(!sys_rand_seeded))
1033 		sys_rand_seed();
1034 	return random();
1035 }
1036 
1037 uint32_t
sys_rand32(void)1038 sys_rand32(void) {
1039 	if (unlikely(!sys_rand_seeded))
1040 		sys_rand_seed();
1041 	return random();
1042 }
1043 
1044 /* vim: set noet ft=c: */
1045 
1046