mirror of
https://github.com/sonertari/SSLproxy
synced 2024-11-06 09:20:26 +00:00
677 lines
15 KiB
C
677 lines
15 KiB
C
/*-
|
|
* SSLsplit - transparent SSL/TLS interception
|
|
* https://www.roe.ch/SSLsplit
|
|
*
|
|
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "sys.h"
|
|
|
|
#include "log.h"
|
|
#include "defaults.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/file.h>
|
|
#include <sys/un.h>
|
|
#include <sys/time.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <fcntl.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <fts.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#ifndef _SC_NPROCESSORS_ONLN
|
|
#include <sys/sysctl.h>
|
|
#endif /* !_SC_NPROCESSORS_ONLN */
|
|
|
|
#if HAVE_DARWIN_LIBPROC
|
|
#include <libproc.h>
|
|
#endif
|
|
|
|
#include <event2/util.h>
|
|
|
|
/*
|
|
* Permanently drop from root privileges to an unprivileged user account.
|
|
* Sets the real, effective and stored user and group ID and the list of
|
|
* ancillary groups. This is only safe if the effective user ID is 0.
|
|
* If username is unset and the effective uid != uid, drop privs to uid.
|
|
* This is to support setuid bit configurations.
|
|
* If groupname is set, it will be used instead of the user's default primary
|
|
* group.
|
|
* If jaildir is set, also chroot to jaildir after reading system files
|
|
* but before dropping privileges.
|
|
* Returns 0 on success, -1 on failure.
|
|
*/
|
|
int
|
|
sys_privdrop(const char *username, const char *groupname, const char *jaildir)
|
|
{
|
|
struct passwd *pw = NULL;
|
|
struct group *gr = NULL;
|
|
int ret = -1;
|
|
|
|
if (groupname) {
|
|
errno = 0;
|
|
if (!(gr = getgrnam(groupname))) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to getgrnam group '%s': %s\n",
|
|
groupname, strerror(errno));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (username) {
|
|
errno = 0;
|
|
if (!(pw = getpwnam(username))) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to getpwnam user '%s': %s\n",
|
|
username, strerror(errno));
|
|
goto error;
|
|
}
|
|
|
|
if (gr != NULL) {
|
|
pw->pw_gid = gr->gr_gid;
|
|
}
|
|
|
|
if (initgroups(username, pw->pw_gid) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to initgroups user '%s': %s\n",
|
|
username, strerror(errno));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (jaildir) {
|
|
if (chroot(jaildir) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to chroot to '%s': %s\n",
|
|
jaildir, strerror(errno));
|
|
goto error;
|
|
}
|
|
if (chdir("/") == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to chdir to '/': %s\n",
|
|
strerror(errno));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (username) {
|
|
if (setgid(pw->pw_gid) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to setgid to %i: %s\n",
|
|
pw->pw_gid, strerror(errno));
|
|
goto error;
|
|
}
|
|
if (setuid(pw->pw_uid) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to setuid to %i: %s\n",
|
|
pw->pw_uid, strerror(errno));
|
|
goto error;
|
|
}
|
|
} else if (getuid() != geteuid()) {
|
|
if (setuid(getuid()) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to setuid(getuid()): %s\n",
|
|
strerror(errno));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
error:
|
|
if (pw) {
|
|
endpwent();
|
|
}
|
|
if (gr) {
|
|
endgrent();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* If the user exists and on successful lookup, return 0 and if uid != NULL,
|
|
* write the uid of *username* to the value pointed to by uid.
|
|
* Return -1 on failure or if the user does not exist.
|
|
*/
|
|
int
|
|
sys_uid(const char *username, uid_t *uid)
|
|
{
|
|
struct passwd *pw;
|
|
int rv;
|
|
|
|
errno = 0;
|
|
if (!(pw = getpwnam(username))) {
|
|
if (errno != 0 && errno != ENOENT) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to load user '%s': %s (%i)\n",
|
|
username, strerror(errno), errno);
|
|
}
|
|
rv = -1;
|
|
} else {
|
|
if (uid)
|
|
*uid = pw->pw_uid;
|
|
rv = 0;
|
|
}
|
|
endpwent();
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Returns 1 if username can be loaded from user database, 0 otherwise.
|
|
*/
|
|
int
|
|
sys_isuser(const char *username)
|
|
{
|
|
return sys_uid(username, NULL) == 0;
|
|
}
|
|
|
|
/*
|
|
* If the group exists and on successful lookup, return 0 and if gid != NULL,
|
|
* write the gid of *groupname* to the value pointed to by gid.
|
|
* Return -1 on failure or if the group does not exist.
|
|
*/
|
|
int
|
|
sys_gid(const char *groupname, gid_t *gid)
|
|
{
|
|
struct group *gr;
|
|
int rv;
|
|
|
|
errno = 0;
|
|
if (!(gr = getgrnam(groupname))) {
|
|
if (errno != 0 && errno != ENOENT) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to load group '%s': %s (%i)\n",
|
|
groupname, strerror(errno), errno);
|
|
}
|
|
rv = -1;
|
|
} else {
|
|
if (gid)
|
|
*gid = gr->gr_gid;
|
|
rv = 0;
|
|
}
|
|
endgrent();
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Returns 1 if groupname can be loaded from group database, 0 otherwise.
|
|
*/
|
|
int
|
|
sys_isgroup(const char *groupname)
|
|
{
|
|
return sys_gid(groupname, NULL) == 0;
|
|
}
|
|
|
|
/*
|
|
* Returns 1 if username is equivalent to the current effective UID.
|
|
* Returns 0 otherwise.
|
|
*/
|
|
int
|
|
sys_isgeteuid(const char *username)
|
|
{
|
|
uid_t uid;
|
|
|
|
if (sys_uid(username, &uid) == -1)
|
|
return 0;
|
|
if (uid == geteuid())
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Open and lock process ID file fn.
|
|
* Returns open file descriptor on success or -1 on errors.
|
|
*/
|
|
int
|
|
sys_pidf_open(const char *fn)
|
|
{
|
|
int fd;
|
|
|
|
if ((fd = open(fn, O_RDWR|O_CREAT, DFLT_PIDFMODE)) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to open '%s': %s\n", fn,
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
|
|
log_err_level_printf(LOG_CRIT, "Failed to lock '%s': %s\n", fn,
|
|
strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* Write process ID to open process ID file descriptor fd.
|
|
* Returns 0 on success, -1 on errors.
|
|
*/
|
|
int
|
|
sys_pidf_write(int fd)
|
|
{
|
|
char pidbuf[4*sizeof(pid_t)];
|
|
int rv;
|
|
ssize_t n;
|
|
|
|
rv = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
|
|
if (rv == -1 || rv >= (int)sizeof(pidbuf))
|
|
return -1;
|
|
|
|
n = write(fd, pidbuf, strlen(pidbuf));
|
|
if (n < (ssize_t)strlen(pidbuf))
|
|
return -1;
|
|
|
|
rv = fsync(fd);
|
|
if (rv == -1)
|
|
return -1;
|
|
|
|
rv = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
|
if (rv == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Close and remove open process ID file before quitting.
|
|
*/
|
|
void
|
|
sys_pidf_close(int fd, const char *fn)
|
|
{
|
|
unlink(fn);
|
|
close(fd);
|
|
}
|
|
|
|
/*
|
|
* Determine address family of addr
|
|
*/
|
|
int
|
|
sys_get_af(const char *addr)
|
|
{
|
|
if (strstr(addr, ":"))
|
|
return AF_INET6;
|
|
else if (!strpbrk(addr, "abcdefghijklmnopqrstu"
|
|
"vwxyzABCDEFGHIJKLMNOP"
|
|
"QRSTUVWXYZ-"))
|
|
return AF_INET;
|
|
else
|
|
return AF_UNSPEC;
|
|
}
|
|
|
|
/*
|
|
* Parse an ascii host/IP and port tuple into a sockaddr_storage.
|
|
* On success, returns address family and fills in addr, addrlen.
|
|
* Returns -1 on error.
|
|
*/
|
|
int
|
|
sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
|
|
char *naddr, char *nport, int af, int flags)
|
|
{
|
|
struct evutil_addrinfo hints;
|
|
struct evutil_addrinfo *ai;
|
|
int rv;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = af;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
|
|
rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
|
|
if (rv != 0) {
|
|
log_err_level_printf(LOG_CRIT, "Cannot resolve address '%s' port '%s': %s\n",
|
|
naddr, nport, gai_strerror(rv));
|
|
return -1;
|
|
}
|
|
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
|
|
*addrlen = ai->ai_addrlen;
|
|
af = ai->ai_family;
|
|
freeaddrinfo(ai);
|
|
return af;
|
|
}
|
|
|
|
/*
|
|
* Converts an IPv4/IPv6 sockaddr into printable string representations of the
|
|
* host and the service (port) part. Writes allocated buffers to *host and
|
|
* *serv which must both be freed by the caller. Neither *host nor *port are
|
|
* freed by this function before newly allocating.
|
|
* Returns 0 on success, -1 otherwise. When -1 is returned, pointers in *host
|
|
* and *serv are invalid and must not be used nor freed by the caller.
|
|
*/
|
|
int
|
|
sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen,
|
|
char **host, char **serv)
|
|
{
|
|
char tmphost[INET6_ADDRSTRLEN];
|
|
int rv;
|
|
size_t hostsz;
|
|
|
|
*serv = malloc(6); /* max decimal digits of short plus terminator */
|
|
if (!*serv) {
|
|
log_err_level_printf(LOG_CRIT, "Cannot allocate memory\n");
|
|
return -1;
|
|
}
|
|
rv = getnameinfo(addr, addrlen,
|
|
tmphost, sizeof(tmphost),
|
|
*serv, 6,
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
|
if (rv != 0) {
|
|
log_err_level_printf(LOG_CRIT, "Cannot get nameinfo for socket address: %s\n",
|
|
gai_strerror(rv));
|
|
free(*serv);
|
|
return -1;
|
|
}
|
|
hostsz = strlen(tmphost) + 1; /* including terminator */
|
|
*host = malloc(hostsz);
|
|
if (!*host) {
|
|
log_err_level_printf(LOG_CRIT, "Cannot allocate memory\n");
|
|
free(*serv);
|
|
return -1;
|
|
}
|
|
memcpy(*host, tmphost, hostsz);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sanitizes a valid IPv4 or IPv6 address for use in a filename, i.e. removes
|
|
* characters that are invalid on NTFS and replaces them with more innocent
|
|
* characters. The function assumes that the input is a valid IPv4 or IPv6
|
|
* address; it is not a generic filename sanitizer.
|
|
*
|
|
* Returns a copy of string s that must be freed by the caller.
|
|
*
|
|
* Invalid NTFS characters are < > : " / \ | ? * according to
|
|
* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247.aspx
|
|
*/
|
|
char *
|
|
sys_ip46str_sanitize(const char *s)
|
|
{
|
|
char *copy, *p;
|
|
|
|
copy = strdup(s);
|
|
if (!copy)
|
|
return NULL;
|
|
p = copy;
|
|
while (*p) {
|
|
switch (*p) {
|
|
case ':':
|
|
case '%':
|
|
*p = '_';
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
return copy;
|
|
}
|
|
|
|
/*
|
|
* Returns 1 if path points to an existing directory node in the filesystem.
|
|
* Returns 0 if path is NULL, does not exist, or points to a file of some kind.
|
|
*/
|
|
int
|
|
sys_isdir(const char *path)
|
|
{
|
|
struct stat s;
|
|
|
|
if (stat(path, &s) == -1) {
|
|
if (errno != ENOENT) {
|
|
log_err_level_printf(LOG_CRIT, "Error stating file: %s (%i)\n",
|
|
strerror(errno), errno);
|
|
}
|
|
return 0;
|
|
}
|
|
if (s.st_mode & S_IFDIR)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Create directory including parent directories with mode_t.
|
|
* Mode of existing parent directories is not changed.
|
|
* Returns 0 on success, -1 and sets errno on error.
|
|
*/
|
|
int
|
|
sys_mkpath(const char *path, mode_t mode)
|
|
{
|
|
char parent[strlen(path)+1];
|
|
char *p;
|
|
|
|
memcpy(parent, path, sizeof(parent));
|
|
|
|
p = parent;
|
|
do {
|
|
/* skip leading '/' characters */
|
|
while (*p == '/') p++;
|
|
p = strchr(p, '/');
|
|
if (p) {
|
|
/* overwrite '/' to terminate the string at the next
|
|
* parent directory */
|
|
*p = '\0';
|
|
}
|
|
|
|
struct stat sbuf;
|
|
if (stat(parent, &sbuf) == -1) {
|
|
if (errno == ENOENT) {
|
|
if (mkdir(parent, mode) != 0)
|
|
return -1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else if (!S_ISDIR(sbuf.st_mode)) {
|
|
errno = ENOTDIR;
|
|
return -1;
|
|
}
|
|
|
|
if (p) {
|
|
/* replace the overwritten slash */
|
|
*p = '/';
|
|
p++;
|
|
}
|
|
} while (p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return realpath(dirname(path)) + / + basename(path) in a newly allocated
|
|
* string. Returns NULL on failure and sets errno to ENOENT if the directory
|
|
* part does not exist.
|
|
*/
|
|
char *
|
|
sys_realdir(const char *path)
|
|
{
|
|
char *sep, *udir, *rdir, *p;
|
|
int rerrno, rv;
|
|
|
|
if (path[0] == '\0') {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
udir = strdup(path);
|
|
if (!udir)
|
|
return NULL;
|
|
|
|
sep = strrchr(udir, '/');
|
|
if (!sep) {
|
|
free(udir);
|
|
rv = asprintf(&udir, "./%s", path);
|
|
if (rv == -1)
|
|
return NULL;
|
|
sep = udir + 1;
|
|
} else if (sep == udir) {
|
|
return udir;
|
|
}
|
|
*sep = '\0';
|
|
rdir = realpath(udir, NULL);
|
|
if (!rdir) {
|
|
rerrno = errno;
|
|
free(udir);
|
|
errno = rerrno;
|
|
return NULL;
|
|
}
|
|
rv = asprintf(&p, "%s/%s", rdir, sep + 1);
|
|
rerrno = errno;
|
|
free(rdir);
|
|
free(udir);
|
|
errno = rerrno;
|
|
if (rv == -1)
|
|
return NULL;
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* Portably get the number of CPU cores online in the system.
|
|
*/
|
|
uint32_t
|
|
sys_get_cpu_cores(void)
|
|
{
|
|
#ifdef _SC_NPROCESSORS_ONLN
|
|
return sysconf(_SC_NPROCESSORS_ONLN);
|
|
#else /* !_SC_NPROCESSORS_ONLN */
|
|
int mib[2];
|
|
uint32_t n;
|
|
size_t len = sizeof(n);
|
|
|
|
mib[0] = CTL_HW;
|
|
mib[1] = HW_AVAILCPU;
|
|
sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
|
|
|
|
if (n < 1) {
|
|
mib[1] = HW_NCPU;
|
|
sysctl(mib, sizeof(mib)/sizeof(int), &n, &len, NULL, 0);
|
|
if (n < 1) {
|
|
n = 1;
|
|
}
|
|
}
|
|
return n;
|
|
#endif /* !_SC_NPROCESSORS_ONLN */
|
|
}
|
|
|
|
/*
|
|
* Send a message and optional file descriptor on a connected AF_UNIX
|
|
* SOCKET_DGRAM socket s. Returns the return value of sendmsg().
|
|
* If fd is -1, no file descriptor is passed.
|
|
*/
|
|
ssize_t
|
|
sys_sendmsgfd(int sock, void *buf, size_t bufsz, int fd)
|
|
{
|
|
struct iovec iov;
|
|
struct msghdr msg;
|
|
struct cmsghdr *cmsg;
|
|
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
ssize_t n;
|
|
|
|
iov.iov_base = buf;
|
|
iov.iov_len = bufsz;
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
if (fd != -1) {
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
if (!cmsg)
|
|
return -1;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
*((int *) CMSG_DATA(cmsg)) = fd;
|
|
} else {
|
|
msg.msg_control = NULL;
|
|
msg.msg_controllen = 0;
|
|
}
|
|
do {
|
|
#ifdef MSG_NOSIGNAL
|
|
n = sendmsg(sock, &msg, MSG_NOSIGNAL);
|
|
#else /* !MSG_NOSIGNAL */
|
|
n = sendmsg(sock, &msg, 0);
|
|
#endif /* !MSG_NOSIGNAL */
|
|
} while (n == -1 && errno == EINTR);
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Receive a message and optional file descriptor on a connected AF_UNIX
|
|
* SOCKET_DGRAM socket s. Returns the return value of recvmsg()/recv()
|
|
* and sets errno to EINVAL if the received message is malformed.
|
|
* If pfd is NULL, no file descriptor is received; if a file descriptor was
|
|
* part of the received message and pfd is NULL, then the kernel will close it.
|
|
*/
|
|
ssize_t
|
|
sys_recvmsgfd(int sock, void *buf, size_t bufsz, int *pfd)
|
|
{
|
|
ssize_t n;
|
|
|
|
if (pfd) {
|
|
struct iovec iov;
|
|
struct msghdr msg;
|
|
struct cmsghdr *cmsg;
|
|
unsigned char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
|
|
iov.iov_base = buf;
|
|
iov.iov_len = bufsz;
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
do {
|
|
n = recvmsg(sock, &msg, 0);
|
|
} while (n == -1 && errno == EINTR);
|
|
if (n <= 0)
|
|
return n;
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
|
if (cmsg->cmsg_level != SOL_SOCKET) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
*pfd = *((int *) CMSG_DATA(cmsg));
|
|
} else {
|
|
*pfd = -1;
|
|
}
|
|
} else {
|
|
do {
|
|
n = recv(sock, buf, bufsz, 0);
|
|
} while (n == -1 && errno == EINTR);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/* vim: set noet ft=c: */
|
|
|