Add support for UNIX Domain Sockets to the C lib. Contributed by Thomas Cort
This commit is contained in:
		
							parent
							
								
									5aff633a0b
								
							
						
					
					
						commit
						f531dba2d2
					
				@ -1,3 +1,6 @@
 | 
			
		||||
20100714:
 | 
			
		||||
	mknod /dev/uds c 18 0
 | 
			
		||||
	chmod 666 /dev/uds
 | 
			
		||||
20100713:
 | 
			
		||||
        /usr/src/etc/rc updated: copy it (or merge it) to /etc/rc.
 | 
			
		||||
        /usr/src/etc/system.conf updated to include boot sys services: copy
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ SRCS+=  \
 | 
			
		||||
	getnetbyaddr.c \
 | 
			
		||||
	getnetbyname.c \
 | 
			
		||||
	getnetent.c \
 | 
			
		||||
	getpeereid.c \
 | 
			
		||||
	getpeername.c \
 | 
			
		||||
	getproto.c \
 | 
			
		||||
	getprotoent.c \
 | 
			
		||||
@ -42,6 +43,7 @@ SRCS+=  \
 | 
			
		||||
	rcmd.c \
 | 
			
		||||
	recv.c \
 | 
			
		||||
	recvfrom.c \
 | 
			
		||||
	recvmsg.c \
 | 
			
		||||
	res_comp.c \
 | 
			
		||||
	res_init.c \
 | 
			
		||||
	res_mkquery.c \
 | 
			
		||||
@ -49,10 +51,12 @@ SRCS+=  \
 | 
			
		||||
	res_send.c \
 | 
			
		||||
	ruserok.c \
 | 
			
		||||
	send.c \
 | 
			
		||||
	sendmsg.c \
 | 
			
		||||
	sendto.c \
 | 
			
		||||
	servxcheck.c \
 | 
			
		||||
	sethostent.c \
 | 
			
		||||
	setsockopt.c \
 | 
			
		||||
	shutdown.c \
 | 
			
		||||
	socket.c \
 | 
			
		||||
	socketpair.c \
 | 
			
		||||
	strcasecmp.c
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,12 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#include <net/netlib.h>
 | 
			
		||||
#include <net/gen/in.h>
 | 
			
		||||
@ -18,6 +20,9 @@
 | 
			
		||||
static int _tcp_accept(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len);
 | 
			
		||||
 | 
			
		||||
static int _uds_accept(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len);
 | 
			
		||||
 | 
			
		||||
int accept(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len)
 | 
			
		||||
{
 | 
			
		||||
@ -28,6 +33,10 @@ int accept(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	r= _uds_accept(socket, address, address_len);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	/* Unfortunately, we have to return EOPNOTSUPP for a socket that
 | 
			
		||||
	 * does not support accept (such as a UDP socket) and ENOTSOCK for
 | 
			
		||||
	 * filedescriptors that do not refer to a socket.
 | 
			
		||||
@ -77,3 +86,45 @@ static int _tcp_accept(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
		getpeername(s1, address, address_len);
 | 
			
		||||
	return s1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_accept(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len)
 | 
			
		||||
{
 | 
			
		||||
	int s1;
 | 
			
		||||
	int r;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
	socklen_t len;
 | 
			
		||||
 | 
			
		||||
	memset(&uds_addr, '\0', sizeof(struct sockaddr_un));
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSADDR, &uds_addr);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (uds_addr.sun_family != AF_UNIX) {
 | 
			
		||||
		errno= EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len= *address_len;
 | 
			
		||||
	if (len > sizeof(struct sockaddr_un))
 | 
			
		||||
		len = sizeof(struct sockaddr_un);
 | 
			
		||||
 | 
			
		||||
	memcpy(address, &uds_addr, len);
 | 
			
		||||
	*address_len= len;
 | 
			
		||||
 | 
			
		||||
	s1= open(UDS_DEVICE, O_RDWR);
 | 
			
		||||
	if (s1 == -1)
 | 
			
		||||
		return s1;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(s1, NWIOSUDSACCEPT, address);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		int ioctl_errno = errno;
 | 
			
		||||
		close(s1);
 | 
			
		||||
		errno = ioctl_errno;
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,14 @@
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#include <net/gen/in.h>
 | 
			
		||||
@ -10,6 +16,10 @@
 | 
			
		||||
#include <net/gen/tcp_io.h>
 | 
			
		||||
#include <net/gen/udp.h>
 | 
			
		||||
#include <net/gen/udp_io.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#include <minix/config.h>
 | 
			
		||||
#include <minix/const.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
@ -17,12 +27,15 @@ static int _tcp_bind(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len, nwio_tcpconf_t *tcpconfp);
 | 
			
		||||
static int _udp_bind(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len, nwio_udpopt_t *udpoptp);
 | 
			
		||||
static int _uds_bind(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len, struct sockaddr_un *uds_addr);
 | 
			
		||||
 | 
			
		||||
int bind(int socket, const struct sockaddr *address, socklen_t address_len)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpconf_t tcpconf;
 | 
			
		||||
	nwio_udpopt_t udpopt;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGTCPCONF, &tcpconf);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
@ -50,6 +63,14 @@ int bind(int socket, const struct sockaddr *address, socklen_t address_len)
 | 
			
		||||
		return _udp_bind(socket, address, address_len, &udpopt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSADDR, &uds_addr);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
			return r;
 | 
			
		||||
		return _uds_bind(socket, address, address_len, &uds_addr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "bind: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -151,3 +172,183 @@ static int _udp_bind(int socket, const struct sockaddr *address,
 | 
			
		||||
	r= ioctl(socket, NWIOSUDPOPT, &udpopt);
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int in_group(uid_t uid, gid_t gid)
 | 
			
		||||
{
 | 
			
		||||
	int r, i;
 | 
			
		||||
	int size;
 | 
			
		||||
	gid_t *list;
 | 
			
		||||
 | 
			
		||||
	size = sysconf(_SC_NGROUPS_MAX);
 | 
			
		||||
	list = malloc(size * sizeof(gid_t));
 | 
			
		||||
 | 
			
		||||
	if (list == NULL) {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= getgroups(size, list);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		free(list);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < r; i++) {
 | 
			
		||||
		if (gid == list[i]) {
 | 
			
		||||
			free(list);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(list);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int _uds_bind(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len, struct sockaddr_un *uds_addr)
 | 
			
		||||
{
 | 
			
		||||
	mode_t bits, perm_bits, access_desired;
 | 
			
		||||
	struct stat buf;
 | 
			
		||||
	uid_t euid;
 | 
			
		||||
	gid_t egid;
 | 
			
		||||
	char real_sun_path[PATH_MAX+1];
 | 
			
		||||
	char *realpath_result;
 | 
			
		||||
	int i, r, shift;
 | 
			
		||||
	int null_found;
 | 
			
		||||
	int did_mknod;
 | 
			
		||||
 | 
			
		||||
	if (address == NULL) {
 | 
			
		||||
		errno = EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* sun_family is always supposed to be AF_UNIX */
 | 
			
		||||
	if (((struct sockaddr_un *) address)->sun_family != AF_UNIX) {
 | 
			
		||||
		errno = EAFNOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* an empty path is not supported */
 | 
			
		||||
	if (((struct sockaddr_un *) address)->sun_path[0] == '\0') {
 | 
			
		||||
		errno = ENOENT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* the path must be a null terminated string for realpath to work */
 | 
			
		||||
	for (null_found = i = 0;
 | 
			
		||||
		i < sizeof(((struct sockaddr_un *) address)->sun_path); i++) {
 | 
			
		||||
		if (((struct sockaddr_un *) address)->sun_path[i] == '\0') {
 | 
			
		||||
			null_found = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!null_found) {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Get the realpath(3) of the socket file.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	realpath_result = realpath(((struct sockaddr_un *) address)->sun_path,
 | 
			
		||||
						real_sun_path);
 | 
			
		||||
	if (realpath_result == NULL) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
 | 
			
		||||
		errno = ENAMETOOLONG;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strcpy(((struct sockaddr_un *) address)->sun_path, real_sun_path);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * input parameters look good -- create the socket file on the 
 | 
			
		||||
	 * file system
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	did_mknod = 0;
 | 
			
		||||
 | 
			
		||||
	r = mknod(((struct sockaddr_un *) address)->sun_path,
 | 
			
		||||
		S_IFSOCK|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0);
 | 
			
		||||
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		if (errno == EEXIST) {
 | 
			
		||||
			/* file already exists, verify that it is a socket */
 | 
			
		||||
 | 
			
		||||
			r = stat(((struct sockaddr_un *) address)->sun_path,
 | 
			
		||||
								&buf);
 | 
			
		||||
			if (r == -1) {
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!S_ISSOCK(buf.st_mode)) {
 | 
			
		||||
				errno = EADDRINUSE;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* check permissions the permissions of the 
 | 
			
		||||
			 * socket file.
 | 
			
		||||
			 */
 | 
			
		||||
 | 
			
		||||
			/* read + write access */
 | 
			
		||||
			access_desired = R_BIT | W_BIT;
 | 
			
		||||
 | 
			
		||||
			euid = geteuid();
 | 
			
		||||
			egid = getegid();
 | 
			
		||||
 | 
			
		||||
			if (euid == -1 || egid == -1) {
 | 
			
		||||
				errno = EACCES;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			bits = buf.st_mode;
 | 
			
		||||
 | 
			
		||||
			if (euid == ((uid_t) 0)) {
 | 
			
		||||
				perm_bits = R_BIT | W_BIT;
 | 
			
		||||
			} else {
 | 
			
		||||
				if (euid == buf.st_uid) {
 | 
			
		||||
					shift = 6; /* owner */
 | 
			
		||||
				} else if (egid == buf.st_gid) {
 | 
			
		||||
					shift = 3; /* group */
 | 
			
		||||
				} else if (in_group(euid, buf.st_gid)) {
 | 
			
		||||
					shift = 3; /* suppl. groups */
 | 
			
		||||
				} else {
 | 
			
		||||
					shift = 0; /* other */
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				perm_bits = 
 | 
			
		||||
				(bits >> shift) & (R_BIT | W_BIT | X_BIT);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ((perm_bits | access_desired) != perm_bits) {
 | 
			
		||||
				errno = EACCES;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* if we get here permissions are OK */
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		did_mknod = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* perform the bind */
 | 
			
		||||
	r= ioctl(socket, NWIOSUDSADDR, (void *) address);
 | 
			
		||||
 | 
			
		||||
	if (r == -1 && did_mknod) {
 | 
			
		||||
 | 
			
		||||
		/* bind() failed in pfs, so we roll back the 
 | 
			
		||||
		 * file system change
 | 
			
		||||
		 */
 | 
			
		||||
		unlink(((struct sockaddr_un *) address)->sun_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,13 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#include <net/gen/in.h>
 | 
			
		||||
@ -12,12 +16,16 @@
 | 
			
		||||
#include <net/gen/udp.h>
 | 
			
		||||
#include <net/gen/udp_io.h>
 | 
			
		||||
 | 
			
		||||
#include <minix/const.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static int _tcp_connect(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len, nwio_tcpconf_t *tcpconfp);
 | 
			
		||||
static int _udp_connect(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len, nwio_udpopt_t *udpoptp);
 | 
			
		||||
static int _uds_connect(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len);
 | 
			
		||||
 | 
			
		||||
int connect(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len)
 | 
			
		||||
@ -47,6 +55,21 @@ int connect(int socket, const struct sockaddr *address,
 | 
			
		||||
		}
 | 
			
		||||
		return _udp_connect(socket, address, address_len, &udpopt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= _uds_connect(socket, address, address_len);
 | 
			
		||||
	if (r != -1 ||
 | 
			
		||||
		(errno != ENOTTY && errno != EBADIOCTL &&
 | 
			
		||||
		 errno != EAFNOSUPPORT))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "connect: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -136,3 +159,148 @@ static int _udp_connect(int socket, const struct sockaddr *address,
 | 
			
		||||
	r= ioctl(socket, NWIOSUDPOPT, &udpopt);
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int in_group(uid_t uid, gid_t gid)
 | 
			
		||||
{
 | 
			
		||||
	int r, i;
 | 
			
		||||
	int size;
 | 
			
		||||
	gid_t *list;
 | 
			
		||||
 | 
			
		||||
	size = sysconf(_SC_NGROUPS_MAX);
 | 
			
		||||
	list = malloc(size * sizeof(gid_t));
 | 
			
		||||
 | 
			
		||||
	if (list == NULL) {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= getgroups(size, list);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		free(list);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < r; i++) {
 | 
			
		||||
		if (gid == list[i]) {
 | 
			
		||||
			free(list);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(list);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_connect(int socket, const struct sockaddr *address,
 | 
			
		||||
	socklen_t address_len)
 | 
			
		||||
{
 | 
			
		||||
	mode_t bits, perm_bits, access_desired;
 | 
			
		||||
	struct stat buf;
 | 
			
		||||
	uid_t euid;
 | 
			
		||||
	gid_t egid;
 | 
			
		||||
	char real_sun_path[PATH_MAX+1];
 | 
			
		||||
	char *realpath_result;
 | 
			
		||||
	int i, r, shift;
 | 
			
		||||
	int null_found;
 | 
			
		||||
 | 
			
		||||
	if (address == NULL) {
 | 
			
		||||
		errno = EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* sun_family is always supposed to be AF_UNIX */
 | 
			
		||||
	if (((struct sockaddr_un *) address)->sun_family != AF_UNIX) {
 | 
			
		||||
		errno = EAFNOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* an empty path is not supported */
 | 
			
		||||
	if (((struct sockaddr_un *) address)->sun_path[0] == '\0') {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* the path must be a null terminated string for realpath to work */
 | 
			
		||||
	for (null_found = i = 0;
 | 
			
		||||
		i < sizeof(((struct sockaddr_un *) address)->sun_path); i++) {
 | 
			
		||||
 | 
			
		||||
		if (((struct sockaddr_un *) address)->sun_path[i] == '\0') {
 | 
			
		||||
			null_found = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!null_found) {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Get the realpath(3) of the socket file.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	realpath_result = realpath(((struct sockaddr_un *) address)->sun_path,
 | 
			
		||||
							real_sun_path);
 | 
			
		||||
	if (realpath_result == NULL) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
 | 
			
		||||
		errno = ENAMETOOLONG;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strcpy(((struct sockaddr_un *) address)->sun_path, real_sun_path);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * input parameters look good -- check the permissions of the 
 | 
			
		||||
	 * socket file. emulate eaccess() (i.e. the access(2) function 
 | 
			
		||||
	 * with effective UID/GID).
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	access_desired = R_BIT | W_BIT; /* read + write access */
 | 
			
		||||
 | 
			
		||||
	euid = geteuid();
 | 
			
		||||
	egid = getegid();
 | 
			
		||||
 | 
			
		||||
	if (euid == -1 || egid == -1) {
 | 
			
		||||
		errno = EACCES;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= stat(((struct sockaddr_un *) address)->sun_path, &buf);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!S_ISSOCK(buf.st_mode)) {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bits = buf.st_mode;
 | 
			
		||||
 | 
			
		||||
	if (euid == ((uid_t) 0)) {
 | 
			
		||||
		perm_bits = R_BIT | W_BIT;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (euid == buf.st_uid) {
 | 
			
		||||
			shift = 6; /* owner */
 | 
			
		||||
		} else if (egid == buf.st_gid) {
 | 
			
		||||
			shift = 3; /* group */
 | 
			
		||||
		} else if (in_group(euid, buf.st_gid)) {
 | 
			
		||||
			shift = 3; /* suppl. groups */
 | 
			
		||||
		} else {
 | 
			
		||||
			shift = 0; /* other */
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((perm_bits | access_desired) != perm_bits) {
 | 
			
		||||
		errno = EACCES;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* perform the connect */
 | 
			
		||||
	r= ioctl(socket, NWIOSUDSCONN, (void *) address);
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								lib/libc/ip/getpeereid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/libc/ip/getpeereid.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * get the effective user ID and effective group ID of a peer
 | 
			
		||||
 * connected through a Unix domain socket.
 | 
			
		||||
 */
 | 
			
		||||
int getpeereid(int sd, uid_t *euid, gid_t *egid) {
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct ucred cred;
 | 
			
		||||
	socklen_t ucred_length;
 | 
			
		||||
 | 
			
		||||
	/* Initialize Data Structures */
 | 
			
		||||
	ucred_length = sizeof(struct ucred);
 | 
			
		||||
	memset(&cred, '\0', ucred_length);
 | 
			
		||||
 | 
			
		||||
	/* Validate Input Parameters */
 | 
			
		||||
	if (euid == NULL || egid == NULL) {
 | 
			
		||||
		errno = EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	} /* getsockopt will handle validating 'sd' */
 | 
			
		||||
 | 
			
		||||
	/* Get the credentials of the peer at the other end of 'sd' */
 | 
			
		||||
	rc = getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &cred, &ucred_length);
 | 
			
		||||
	if (rc == 0) {
 | 
			
		||||
		/* Success - return the results */
 | 
			
		||||
		*euid = cred.uid;
 | 
			
		||||
		*egid = cred.gid;
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Failure - getsockopt takes care of setting errno */
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -9,20 +9,25 @@
 | 
			
		||||
#include <net/gen/tcp.h>
 | 
			
		||||
#include <net/gen/tcp_io.h>
 | 
			
		||||
#include <net/gen/udp.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static int _tcp_getpeername(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len, nwio_tcpconf_t *tcpconfp);
 | 
			
		||||
 | 
			
		||||
static int _uds_getpeername(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len, struct sockaddr_un *uds_addr);
 | 
			
		||||
 | 
			
		||||
int getpeername(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpconf_t tcpconf;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGTCPCONF, &tcpconf);
 | 
			
		||||
	if (r != -1 || errno != ENOTTY)
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
@ -33,6 +38,19 @@ int getpeername(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
			&tcpconf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSPADDR, &uds_addr);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		return _uds_getpeername(socket, address, address_len,
 | 
			
		||||
			&uds_addr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "getpeername: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -67,3 +85,23 @@ static int _tcp_getpeername(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_getpeername(int socket, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len, struct sockaddr_un *uds_addr)
 | 
			
		||||
{
 | 
			
		||||
	socklen_t len;
 | 
			
		||||
 | 
			
		||||
	if (uds_addr->sun_family != AF_UNIX)
 | 
			
		||||
	{
 | 
			
		||||
		errno= ENOTCONN;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len= *address_len;
 | 
			
		||||
	if (len > sizeof(struct sockaddr_un))
 | 
			
		||||
		len = sizeof(struct sockaddr_un);
 | 
			
		||||
 | 
			
		||||
	memcpy(address, uds_addr, len);
 | 
			
		||||
	*address_len= len;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,31 +18,68 @@
 | 
			
		||||
#include <net/gen/tcp.h>
 | 
			
		||||
#include <net/gen/tcp_io.h>
 | 
			
		||||
#include <net/gen/udp.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   getsockname...
 | 
			
		||||
*/
 | 
			
		||||
int getsockname(int fd, struct sockaddr *_RESTRICT address, 
 | 
			
		||||
static int _tcp_getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
   socklen_t *_RESTRICT address_len, nwio_tcpconf_t *tcpconfp);
 | 
			
		||||
 | 
			
		||||
static int _uds_getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
   socklen_t *_RESTRICT address_len, struct sockaddr_un *uds_addr);
 | 
			
		||||
 | 
			
		||||
int getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
   socklen_t *_RESTRICT address_len)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpconf_t tcpconf;
 | 
			
		||||
	socklen_t len;
 | 
			
		||||
	struct sockaddr_in sin;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	fprintf(stderr,"mnx_getsockname: ioctl fd %d.\n", fd);
 | 
			
		||||
#endif
 | 
			
		||||
	if (ioctl(fd, NWIOGTCPCONF, &tcpconf)==-1) {
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	   fprintf(stderr,"mnx_getsockname: error %d\n", errno);
 | 
			
		||||
 | 
			
		||||
	r= ioctl(fd, NWIOGTCPCONF, &tcpconf);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return _tcp_getsockname(fd, address, address_len, &tcpconf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(fd, NWIOGUDSADDR, &uds_addr);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return _uds_getsockname(fd, address, address_len, &uds_addr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "getsockname: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
	   return (-1);
 | 
			
		||||
	   }
 | 
			
		||||
 | 
			
		||||
	errno= ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int _tcp_getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
   socklen_t *_RESTRICT address_len, nwio_tcpconf_t *tcpconf)
 | 
			
		||||
{
 | 
			
		||||
	socklen_t len;
 | 
			
		||||
	struct sockaddr_in sin;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG1
 | 
			
		||||
	fprintf(stderr, "mnx_getsockname: from %s, %u",
 | 
			
		||||
			inet_ntoa(tcpconf.nwtc_remaddr),
 | 
			
		||||
@ -51,14 +88,11 @@ int getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
			inet_ntoa(tcpconf.nwtc_locaddr),
 | 
			
		||||
			ntohs(tcpconf.nwtc_locport));
 | 
			
		||||
#endif
 | 
			
		||||
/*
 | 
			
		||||
	addr->sin_addr.s_addr = tcpconf.nwtc_remaddr ;
 | 
			
		||||
	addr->sin_port = tcpconf.nwtc_locport;
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
	memset(&sin, '\0', sizeof(sin));
 | 
			
		||||
	sin.sin_family= AF_INET;
 | 
			
		||||
	sin.sin_addr.s_addr= tcpconf.nwtc_locaddr ;
 | 
			
		||||
	sin.sin_port= tcpconf.nwtc_locport;
 | 
			
		||||
	sin.sin_addr.s_addr= tcpconf->nwtc_locaddr ;
 | 
			
		||||
	sin.sin_port= tcpconf->nwtc_locport;
 | 
			
		||||
 | 
			
		||||
	len= *address_len;
 | 
			
		||||
	if (len > sizeof(sin))
 | 
			
		||||
@ -69,10 +103,23 @@ int getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_getsockname(int fd, struct sockaddr *_RESTRICT address,
 | 
			
		||||
   socklen_t *_RESTRICT address_len, struct sockaddr_un *uds_addr)
 | 
			
		||||
{
 | 
			
		||||
	socklen_t len;
 | 
			
		||||
 | 
			
		||||
	if (uds_addr->sun_family != AF_UNIX)
 | 
			
		||||
	{
 | 
			
		||||
		errno= EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len= *address_len;
 | 
			
		||||
	if (len > sizeof(struct sockaddr_un))
 | 
			
		||||
		len = sizeof(struct sockaddr_un);
 | 
			
		||||
 | 
			
		||||
	memcpy(address, uds_addr, len);
 | 
			
		||||
	*address_len= len;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/ucred.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
 | 
			
		||||
#include <net/gen/in.h>
 | 
			
		||||
@ -13,12 +14,16 @@
 | 
			
		||||
#include <net/gen/udp.h>
 | 
			
		||||
#include <net/gen/udp_io.h>
 | 
			
		||||
 | 
			
		||||
#include <minix/type.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static int _tcp_getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	void *_RESTRICT option_value, socklen_t *_RESTRICT option_len);
 | 
			
		||||
static int _udp_getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	void *_RESTRICT option_value, socklen_t *_RESTRICT option_len);
 | 
			
		||||
static int _uds_getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	void *_RESTRICT option_value, socklen_t *_RESTRICT option_len);
 | 
			
		||||
static void getsockopt_copy(void *return_value, size_t return_len,
 | 
			
		||||
	void *_RESTRICT option_value, socklen_t *_RESTRICT option_len);
 | 
			
		||||
 | 
			
		||||
@ -28,9 +33,10 @@ int getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpopt_t tcpopt;
 | 
			
		||||
	nwio_udpopt_t udpopt;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGTCPOPT, &tcpopt);
 | 
			
		||||
	if (r != -1 || errno != ENOTTY)
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
@ -42,7 +48,7 @@ int getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDPOPT, &udpopt);
 | 
			
		||||
	if (r != -1 || errno != ENOTTY)
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
@ -53,6 +59,19 @@ int getsockopt(int socket, int level, int option_name,
 | 
			
		||||
			option_value, option_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSADDR, &uds_addr);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		return _uds_getsockopt(socket, level, option_name,
 | 
			
		||||
			option_value, option_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "getsockopt: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -141,7 +160,7 @@ static int _udp_getsockopt(int socket, int level, int option_name,
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_TYPE)
 | 
			
		||||
	{
 | 
			
		||||
		i = SOCK_DGRAM;	/* this is a TCP socket */
 | 
			
		||||
		i = SOCK_DGRAM;	/* this is a UDP socket */
 | 
			
		||||
		getsockopt_copy(&i, sizeof(i), option_value, option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
@ -153,3 +172,83 @@ static int _udp_getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	errno= ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_getsockopt(int socket, int level, int option_name,
 | 
			
		||||
	void *_RESTRICT option_value, socklen_t *_RESTRICT option_len)
 | 
			
		||||
{
 | 
			
		||||
	int i, r;
 | 
			
		||||
	size_t size;
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
 | 
			
		||||
	{
 | 
			
		||||
 		r= ioctl(socket, NWIOGUDSRCVBUF, &size);
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		getsockopt_copy(&size, sizeof(size), option_value, option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
 | 
			
		||||
	{
 | 
			
		||||
 		r= ioctl(socket, NWIOGUDSSNDBUF, &size);
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		getsockopt_copy(&size, sizeof(size), option_value, option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_TYPE)
 | 
			
		||||
	{
 | 
			
		||||
 		r= ioctl(socket, NWIOGUDSSOTYPE, &i);
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		getsockopt_copy(&i, sizeof(i), option_value, option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_PEERCRED)
 | 
			
		||||
	{
 | 
			
		||||
		struct ucred cred;
 | 
			
		||||
 | 
			
		||||
		r= ioctl(socket, NWIOGUDSPEERCRED, &cred);
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		getsockopt_copy(&cred, sizeof(struct ucred), option_value,
 | 
			
		||||
							option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
 | 
			
		||||
	{
 | 
			
		||||
		i = 1;	/* as long as nobody is listen()ing on the address,
 | 
			
		||||
			 * it can be reused without waiting for a 
 | 
			
		||||
			 * timeout to expire.
 | 
			
		||||
			 */
 | 
			
		||||
		getsockopt_copy(&i, sizeof(i), option_value, option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_PASSCRED)
 | 
			
		||||
	{
 | 
			
		||||
		i = 1;	/* option is always 'on' */
 | 
			
		||||
		getsockopt_copy(&i, sizeof(i), option_value, option_len);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "_uds_getsocketopt: level %d, name %d\n",
 | 
			
		||||
		level, option_name);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	errno= ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,10 @@ int listen(int socket, int backlog)
 | 
			
		||||
	if (r != -1 || errno != EBADIOCTL)
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOSUDSBLOG, &backlog);
 | 
			
		||||
	if (r != -1 || errno != EBADIOCTL)
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "listen: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -5,4 +5,3 @@ ssize_t recv(int socket, void *buffer, size_t length, int flags)
 | 
			
		||||
{
 | 
			
		||||
	return recvfrom(socket, buffer, length, flags, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,12 @@ static ssize_t _tcp_recvfrom(int socket, void *_RESTRICT buffer, size_t length,
 | 
			
		||||
static ssize_t _udp_recvfrom(int socket, void *_RESTRICT buffer, size_t length,
 | 
			
		||||
	int flags, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len, nwio_udpopt_t *udpoptp);
 | 
			
		||||
static ssize_t _uds_recvfrom_conn(int socket, void *_RESTRICT buffer,
 | 
			
		||||
	size_t length, int flags, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len, struct sockaddr_un *uds_addr);
 | 
			
		||||
static ssize_t _uds_recvfrom_dgram(int socket, void *_RESTRICT buffer,
 | 
			
		||||
	size_t length, int flags, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len);
 | 
			
		||||
 | 
			
		||||
ssize_t recvfrom(int socket, void *_RESTRICT buffer, size_t length,
 | 
			
		||||
	int flags, struct sockaddr *_RESTRICT address,
 | 
			
		||||
@ -33,6 +39,8 @@ ssize_t recvfrom(int socket, void *_RESTRICT buffer, size_t length,
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpconf_t tcpconf;
 | 
			
		||||
	nwio_udpopt_t udpopt;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
	int uds_sotype = -1;
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "recvfrom: for fd %d\n", socket);
 | 
			
		||||
@ -56,6 +64,24 @@ ssize_t recvfrom(int socket, void *_RESTRICT buffer, size_t length,
 | 
			
		||||
			address, address_len, &udpopt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSSOTYPE, &uds_sotype);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (uds_sotype == SOCK_DGRAM) {
 | 
			
		||||
			return _uds_recvfrom_dgram(socket, buffer, 
 | 
			
		||||
				length, flags, address, address_len);
 | 
			
		||||
		} else {
 | 
			
		||||
			return _uds_recvfrom_conn(socket, buffer, 
 | 
			
		||||
				length, flags, address, address_len, 
 | 
			
		||||
				&uds_addr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "recvfrom: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -197,3 +223,70 @@ static ssize_t _udp_recvfrom(int socket, void *_RESTRICT buffer, size_t length,
 | 
			
		||||
	return length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_recvfrom_conn(int socket, void *_RESTRICT buffer, 
 | 
			
		||||
	size_t length, int flags, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len, struct sockaddr_un *uds_addr)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	size_t len;
 | 
			
		||||
 | 
			
		||||
	/* for connection oriented unix domain sockets (SOCK_STREAM / 
 | 
			
		||||
	 * SOCK_SEQPACKET)
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (flags != 0)
 | 
			
		||||
	{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "recvfrom(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r = read(socket, buffer, length);
 | 
			
		||||
 | 
			
		||||
	if (r >= 0 && address != NULL)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		len= *address_len;
 | 
			
		||||
		if (len > sizeof(struct sockaddr_un))
 | 
			
		||||
			len= sizeof(struct sockaddr_un);
 | 
			
		||||
		memcpy(address, uds_addr, len);
 | 
			
		||||
		*address_len= sizeof(struct sockaddr_un);
 | 
			
		||||
	}	
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_recvfrom_dgram(int socket, void *_RESTRICT buffer, 
 | 
			
		||||
	size_t length, int flags, struct sockaddr *_RESTRICT address,
 | 
			
		||||
	socklen_t *_RESTRICT address_len)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	size_t len;
 | 
			
		||||
 | 
			
		||||
	/* for connectionless unix domain sockets (SOCK_DGRAM) */
 | 
			
		||||
 | 
			
		||||
	if (flags != 0)
 | 
			
		||||
	{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "recvfrom(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r = read(socket, buffer, length);
 | 
			
		||||
 | 
			
		||||
	if (r >= 0 && address != NULL)
 | 
			
		||||
	{
 | 
			
		||||
		len= *address_len;
 | 
			
		||||
		if (len > sizeof(struct sockaddr_un))
 | 
			
		||||
			len= sizeof(struct sockaddr_un);
 | 
			
		||||
		ioctl(socket, NWIOGUDSFADDR, address);
 | 
			
		||||
		*address_len= sizeof(struct sockaddr_un);
 | 
			
		||||
	}	
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								lib/libc/ip/recvmsg.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lib/libc/ip/recvmsg.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
			
		||||
#undef NDEBUG
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <net/ioctl.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_recvmsg_conn(int socket, struct msghdr *msg, int flags);
 | 
			
		||||
static ssize_t _uds_recvmsg_dgram(int socket, struct msghdr *msg, int flags);
 | 
			
		||||
 | 
			
		||||
ssize_t recvmsg(int socket, struct msghdr *msg, int flags)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	int uds_sotype;
 | 
			
		||||
 | 
			
		||||
	if (msg == NULL) {
 | 
			
		||||
		errno= EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSSOTYPE, &uds_sotype);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (uds_sotype == SOCK_DGRAM) {
 | 
			
		||||
			return _uds_recvmsg_dgram(socket, msg, flags);
 | 
			
		||||
		} else {
 | 
			
		||||
			return _uds_recvmsg_conn(socket, msg, flags);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "recvmsg: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	errno= ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_recvmsg_conn(int socket, struct msghdr *msg, int flags)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	if (flags != 0) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "recvmsg(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r = readv(socket, msg->msg_iov, msg->msg_iovlen);
 | 
			
		||||
 | 
			
		||||
	if (r >= 0 && msg->msg_name && msg->msg_namelen > 0)
 | 
			
		||||
	{
 | 
			
		||||
		getpeername(socket, msg->msg_name, &msg->msg_namelen);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg->msg_flags = 0;
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_recvmsg_dgram(int socket, struct msghdr *msg, int flags)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	if (flags != 0) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "recvmsg(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r = readv(socket, msg->msg_iov, msg->msg_iovlen);
 | 
			
		||||
 | 
			
		||||
	if (r >= 0 && msg->msg_name && msg->msg_namelen > 0)
 | 
			
		||||
	{
 | 
			
		||||
		ioctl(socket, NWIOGUDSFADDR, msg->msg_name);
 | 
			
		||||
		msg->msg_namelen= sizeof(struct sockaddr_un);
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg->msg_flags = 0;
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
@ -5,4 +5,3 @@ ssize_t send(int socket, const void *buffer, size_t length, int flags)
 | 
			
		||||
{
 | 
			
		||||
	return sendto(socket, buffer, length, flags, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										139
									
								
								lib/libc/ip/sendmsg.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								lib/libc/ip/sendmsg.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,139 @@
 | 
			
		||||
#undef NDEBUG
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <net/ioctl.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_sendmsg_conn(int socket, const struct msghdr *msg, 
 | 
			
		||||
	int flags);
 | 
			
		||||
static ssize_t _uds_sendmsg_dgram(int socket, const struct msghdr *msg, 
 | 
			
		||||
	int flags);
 | 
			
		||||
 | 
			
		||||
ssize_t sendmsg(int socket, const struct msghdr *msg, int flags)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	int uds_sotype;
 | 
			
		||||
 | 
			
		||||
	if (msg == NULL) {
 | 
			
		||||
		errno= EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSSOTYPE, &uds_sotype);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (uds_sotype == SOCK_DGRAM) {
 | 
			
		||||
			return _uds_sendmsg_dgram(socket, msg, flags);
 | 
			
		||||
		} else {
 | 
			
		||||
			return _uds_sendmsg_conn(socket, msg, flags);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "sendmsg: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	errno= ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_sendmsg_conn(int socket, const struct msghdr *msg, 
 | 
			
		||||
	int flags)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	if (flags != 0) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "sendmsg(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Silently ignore destination, if given. */
 | 
			
		||||
 | 
			
		||||
	return writev(socket, msg->msg_iov, msg->msg_iovlen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_sendmsg_dgram(int socket, const struct msghdr *msg, 
 | 
			
		||||
	int flags)
 | 
			
		||||
{
 | 
			
		||||
	char real_sun_path[PATH_MAX+1];
 | 
			
		||||
	char *realpath_result;
 | 
			
		||||
	char *dest_addr;
 | 
			
		||||
	int null_found;
 | 
			
		||||
	int i, r;
 | 
			
		||||
 | 
			
		||||
	if (flags != 0) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "sendmsg(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dest_addr = msg->msg_name;
 | 
			
		||||
	if (dest_addr == NULL) {
 | 
			
		||||
		errno= EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* sun_family is always supposed to be AF_UNIX */
 | 
			
		||||
	if (((struct sockaddr_un *) dest_addr)->sun_family != AF_UNIX) {
 | 
			
		||||
		errno = EAFNOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* an empty path is not supported */
 | 
			
		||||
	if (((struct sockaddr_un *) dest_addr)->sun_path[0] == '\0') {
 | 
			
		||||
		errno = ENOENT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* the path must be a null terminated string for realpath to work */
 | 
			
		||||
	for (null_found = i = 0;
 | 
			
		||||
		i < sizeof(((struct sockaddr_un *) dest_addr)->sun_path); i++) {
 | 
			
		||||
		if (((struct sockaddr_un *) dest_addr)->sun_path[i] == '\0') {
 | 
			
		||||
			null_found = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!null_found) {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	realpath_result = realpath(
 | 
			
		||||
		((struct sockaddr_un *) dest_addr)->sun_path, real_sun_path);
 | 
			
		||||
 | 
			
		||||
	if (realpath_result == NULL) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
 | 
			
		||||
		errno = ENAMETOOLONG;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* set the target address */
 | 
			
		||||
	r= ioctl(socket, NWIOSUDSTADDR, (void *) dest_addr);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* do the send */
 | 
			
		||||
	return writev(socket, msg->msg_iov, msg->msg_iovlen);
 | 
			
		||||
}
 | 
			
		||||
@ -24,6 +24,10 @@ static ssize_t _tcp_sendto(int socket, const void *message, size_t length,
 | 
			
		||||
static ssize_t _udp_sendto(int socket, const void *message, size_t length,
 | 
			
		||||
	int flags, const struct sockaddr *dest_addr, socklen_t dest_len,
 | 
			
		||||
	nwio_udpopt_t *udpoptp);
 | 
			
		||||
static ssize_t _uds_sendto_conn(int socket, const void *message, size_t length,
 | 
			
		||||
	int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
 | 
			
		||||
static ssize_t _uds_sendto_dgram(int socket, const void *message, size_t length,
 | 
			
		||||
	int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
 | 
			
		||||
 | 
			
		||||
ssize_t sendto(int socket, const void *message, size_t length, int flags,
 | 
			
		||||
	const struct sockaddr *dest_addr, socklen_t dest_len)
 | 
			
		||||
@ -31,6 +35,8 @@ ssize_t sendto(int socket, const void *message, size_t length, int flags,
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpopt_t tcpopt;
 | 
			
		||||
	nwio_udpopt_t udpopt;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
	int uds_sotype = -1;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGTCPOPT, &tcpopt);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
@ -50,6 +56,24 @@ ssize_t sendto(int socket, const void *message, size_t length, int flags,
 | 
			
		||||
			dest_addr, dest_len, &udpopt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSSOTYPE, &uds_sotype);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			return r;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (uds_sotype == SOCK_DGRAM) {
 | 
			
		||||
 | 
			
		||||
			return _uds_sendto_dgram(socket, message, 
 | 
			
		||||
				length, flags,dest_addr, dest_len);
 | 
			
		||||
		} else {
 | 
			
		||||
 | 
			
		||||
			return _uds_sendto_conn(socket, message,
 | 
			
		||||
				length, flags, dest_addr, dest_len);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "sendto: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -158,3 +182,96 @@ static ssize_t _udp_sendto(int socket, const void *message, size_t length,
 | 
			
		||||
	return length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_sendto_conn(int socket, const void *message, size_t length,
 | 
			
		||||
	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/* for connection oriented unix domain sockets (SOCK_STREAM / 
 | 
			
		||||
	 * SOCK_SEQPACKET)
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (flags != 0) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "sendto(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Silently ignore destination, if given. */
 | 
			
		||||
 | 
			
		||||
	return write(socket, message, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t _uds_sendto_dgram(int socket, const void *message, size_t length,
 | 
			
		||||
	int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
 | 
			
		||||
{
 | 
			
		||||
	char real_sun_path[PATH_MAX+1];
 | 
			
		||||
	char *realpath_result;
 | 
			
		||||
	int null_found;
 | 
			
		||||
	int i, r;
 | 
			
		||||
 | 
			
		||||
	/* for connectionless unix domain sockets (SOCK_DGRAM) */
 | 
			
		||||
 | 
			
		||||
	if (flags != 0) {
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "sendto(uds): flags not implemented\n");
 | 
			
		||||
#endif
 | 
			
		||||
		errno= ENOSYS;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dest_addr == NULL) {
 | 
			
		||||
		errno = EFAULT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* sun_family is always supposed to be AF_UNIX */
 | 
			
		||||
	if (((struct sockaddr_un *) dest_addr)->sun_family != AF_UNIX) {
 | 
			
		||||
		errno = EAFNOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* an empty path is not supported */
 | 
			
		||||
	if (((struct sockaddr_un *) dest_addr)->sun_path[0] == '\0') {
 | 
			
		||||
		errno = ENOENT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* the path must be a null terminated string for realpath to work */
 | 
			
		||||
	for (null_found = i = 0;
 | 
			
		||||
		i < sizeof(((struct sockaddr_un *) dest_addr)->sun_path); i++) {
 | 
			
		||||
		if (((struct sockaddr_un *) dest_addr)->sun_path[i] == '\0') {
 | 
			
		||||
			null_found = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!null_found) {
 | 
			
		||||
		errno = EINVAL;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	realpath_result = realpath(
 | 
			
		||||
		((struct sockaddr_un *) dest_addr)->sun_path, real_sun_path);
 | 
			
		||||
 | 
			
		||||
	if (realpath_result == NULL) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
 | 
			
		||||
		errno = ENAMETOOLONG;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strcpy(((struct sockaddr_un *) dest_addr)->sun_path, real_sun_path);
 | 
			
		||||
 | 
			
		||||
	/* set the target address */
 | 
			
		||||
	r= ioctl(socket, NWIOSUDSTADDR, (void *) dest_addr);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* do the send */
 | 
			
		||||
	return write(socket, message, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,15 +20,19 @@ static int _tcp_setsockopt(int socket, int level, int option_name,
 | 
			
		||||
static int _udp_setsockopt(int socket, int level, int option_name,
 | 
			
		||||
	const void *option_value, socklen_t option_len);
 | 
			
		||||
 | 
			
		||||
static int _uds_setsockopt(int socket, int level, int option_name,
 | 
			
		||||
	const void *option_value, socklen_t option_len);
 | 
			
		||||
 | 
			
		||||
int setsockopt(int socket, int level, int option_name,
 | 
			
		||||
        const void *option_value, socklen_t option_len)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	nwio_tcpopt_t tcpopt;
 | 
			
		||||
	nwio_udpopt_t udpopt;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGTCPOPT, &tcpopt);
 | 
			
		||||
	if (r != -1 || errno != ENOTTY)
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
@ -40,7 +44,7 @@ int setsockopt(int socket, int level, int option_name,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDPOPT, &udpopt);
 | 
			
		||||
	if (r != -1 || errno != ENOTTY)
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
@ -51,6 +55,19 @@ int setsockopt(int socket, int level, int option_name,
 | 
			
		||||
			option_value, option_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSADDR, &uds_addr);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		return _uds_setsockopt(socket, level, option_name,
 | 
			
		||||
			option_value, option_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "setsockopt: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -178,3 +195,76 @@ static int _udp_setsockopt(int socket, int level, int option_name,
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int _uds_setsockopt(int socket, int level, int option_name,
 | 
			
		||||
	const void *option_value, socklen_t option_len)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	size_t size;
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
 | 
			
		||||
	{
 | 
			
		||||
		if (option_len != sizeof(size))
 | 
			
		||||
		{
 | 
			
		||||
			errno= EINVAL;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		size= *(size_t *)option_value;
 | 
			
		||||
		return ioctl(socket, NWIOSUDSRCVBUF, &size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
 | 
			
		||||
	{
 | 
			
		||||
		if (option_len != sizeof(size))
 | 
			
		||||
		{
 | 
			
		||||
			errno= EINVAL;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		size= *(size_t *)option_value;
 | 
			
		||||
		return ioctl(socket, NWIOSUDSSNDBUF, &size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
 | 
			
		||||
	{
 | 
			
		||||
		if (option_len != sizeof(i))
 | 
			
		||||
		{
 | 
			
		||||
			errno= EINVAL;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		i= *(int *)option_value;
 | 
			
		||||
		if (!i)
 | 
			
		||||
		{
 | 
			
		||||
			/* At the moment there is no way to turn off 
 | 
			
		||||
			 * reusing addresses.
 | 
			
		||||
			 */
 | 
			
		||||
			errno= ENOSYS;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (level == SOL_SOCKET && option_name == SO_PASSCRED)
 | 
			
		||||
	{
 | 
			
		||||
		if (option_len != sizeof(i))
 | 
			
		||||
		{
 | 
			
		||||
			errno= EINVAL;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		i= *(int *)option_value;
 | 
			
		||||
		if (!i)
 | 
			
		||||
		{
 | 
			
		||||
			/* credentials can always be received. */
 | 
			
		||||
			errno= ENOSYS;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "_uds_setsocketopt: level %d, name %d\n",
 | 
			
		||||
		level, option_name);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	errno= ENOSYS;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,22 +2,25 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#include <net/gen/in.h>
 | 
			
		||||
#include <net/gen/tcp.h>
 | 
			
		||||
#include <net/gen/tcp_io.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 1
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static int _tcp_shutdown(int socket, int how);
 | 
			
		||||
static int _uds_shutdown(int socket, int how);
 | 
			
		||||
 | 
			
		||||
int shutdown(int socket, int how)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	struct sockaddr_un uds_addr;
 | 
			
		||||
	nwio_tcpconf_t tcpconf;
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGTCPCONF, &tcpconf);
 | 
			
		||||
	if (r != -1 || errno != ENOTTY)
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
@ -26,6 +29,18 @@ int shutdown(int socket, int how)
 | 
			
		||||
		}
 | 
			
		||||
		return _tcp_shutdown(socket, how);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= ioctl(socket, NWIOGUDSADDR, &uds_addr);
 | 
			
		||||
	if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
 | 
			
		||||
	{
 | 
			
		||||
		if (r == -1)
 | 
			
		||||
		{
 | 
			
		||||
			/* Bad file descriptor */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		return _uds_shutdown(socket, how);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "shutdown: not implemented for fd %d\n", socket);
 | 
			
		||||
#endif
 | 
			
		||||
@ -51,4 +66,7 @@ static int _tcp_shutdown(int socket, int how)
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int _uds_shutdown(int socket, int how)
 | 
			
		||||
{
 | 
			
		||||
	return ioctl(socket, NWIOSUDSSHUT, &how);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,12 +6,14 @@
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <net/netlib.h>
 | 
			
		||||
#include <net/ioctl.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static int _tcp_socket(int protocol);
 | 
			
		||||
static int _udp_socket(int protocol);
 | 
			
		||||
static int _uds_socket(int type, int protocol);
 | 
			
		||||
 | 
			
		||||
int socket(int domain, int type, int protocol)
 | 
			
		||||
{
 | 
			
		||||
@ -19,7 +21,7 @@ int socket(int domain, int type, int protocol)
 | 
			
		||||
	fprintf(stderr, "socket: domain %d, type %d, protocol %d\n",
 | 
			
		||||
		domain, type, protocol);
 | 
			
		||||
#endif
 | 
			
		||||
	if (domain != AF_INET)
 | 
			
		||||
	if (domain != AF_INET && domain != AF_UNIX)
 | 
			
		||||
	{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "socket: bad domain %d\n", domain);
 | 
			
		||||
@ -27,10 +29,15 @@ int socket(int domain, int type, int protocol)
 | 
			
		||||
		errno= EAFNOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (type == SOCK_STREAM)
 | 
			
		||||
 | 
			
		||||
	if (domain == AF_UNIX && (type == SOCK_STREAM ||
 | 
			
		||||
				type == SOCK_DGRAM || type == SOCK_SEQPACKET))
 | 
			
		||||
		return _uds_socket(type, protocol);
 | 
			
		||||
 | 
			
		||||
	if (domain == AF_INET && type == SOCK_STREAM)
 | 
			
		||||
		return _tcp_socket(protocol);
 | 
			
		||||
 | 
			
		||||
	if (type == SOCK_DGRAM)
 | 
			
		||||
	if (domain == AF_INET && type == SOCK_DGRAM)
 | 
			
		||||
		return _udp_socket(protocol);
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
@ -88,3 +95,38 @@ static int _udp_socket(int protocol)
 | 
			
		||||
	return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_socket(int type, int protocol)
 | 
			
		||||
{
 | 
			
		||||
	int fd, r;
 | 
			
		||||
	if (protocol != 0)
 | 
			
		||||
	{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "socket(uds): bad protocol %d\n", protocol);
 | 
			
		||||
#endif
 | 
			
		||||
		errno= EPROTONOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fd= open(UDS_DEVICE, O_RDWR);
 | 
			
		||||
	if (fd == -1) {
 | 
			
		||||
		return fd;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* set the type for the socket via ioctl (SOCK_DGRAM, 
 | 
			
		||||
	 * SOCK_STREAM, SOCK_SEQPACKET, etc)
 | 
			
		||||
	 */
 | 
			
		||||
	r= ioctl(fd, NWIOSUDSTYPE, &type);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		int ioctl_errno;
 | 
			
		||||
 | 
			
		||||
		/* if that failed rollback socket creation */
 | 
			
		||||
		ioctl_errno= errno;
 | 
			
		||||
		close(fd);
 | 
			
		||||
 | 
			
		||||
		/* return the error thrown by the call to ioctl */
 | 
			
		||||
		errno= ioctl_errno;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										135
									
								
								lib/libc/ip/socketpair.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								lib/libc/ip/socketpair.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <net/ioctl.h>
 | 
			
		||||
#include <net/netlib.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG 0
 | 
			
		||||
 | 
			
		||||
static int _uds_socketpair(int type, int protocol, int sv[2]);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Create a pair of connected sockets
 | 
			
		||||
 */
 | 
			
		||||
int socketpair(int domain, int type, int protocol, int sv[2]) {
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr, "socketpair: domain %d, type %d, protocol %d\n",
 | 
			
		||||
		domain, type, protocol);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (domain != AF_UNIX)
 | 
			
		||||
	{
 | 
			
		||||
		errno = EAFNOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (domain == AF_UNIX &&
 | 
			
		||||
			(type == SOCK_STREAM || type == SOCK_SEQPACKET))
 | 
			
		||||
		return _uds_socketpair(type, protocol, sv);
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
	fprintf(stderr,
 | 
			
		||||
		"socketpair: nothing for domain %d, type %d, protocol %d\n",
 | 
			
		||||
		domain, type, protocol);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	errno= EPROTOTYPE;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _uds_socketpair(int type, int protocol, int sv[2])
 | 
			
		||||
{
 | 
			
		||||
	dev_t dev;
 | 
			
		||||
	int r, i;
 | 
			
		||||
	struct stat sbuf;
 | 
			
		||||
 | 
			
		||||
	if (protocol != 0)
 | 
			
		||||
	{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
		fprintf(stderr, "socketpair(uds): bad protocol %d\n", protocol);
 | 
			
		||||
#endif
 | 
			
		||||
		errno= EPROTONOSUPPORT;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* in this 'for' loop two unconnected sockets are created */
 | 
			
		||||
	for (i = 0; i < 2; i++) {
 | 
			
		||||
		sv[i]= open(UDS_DEVICE, O_RDWR);
 | 
			
		||||
		if (sv[i] == -1) {
 | 
			
		||||
			int open_errno = errno;
 | 
			
		||||
 | 
			
		||||
			if (i == 1) {
 | 
			
		||||
				/* if we failed to open() the 2nd 
 | 
			
		||||
				 * socket, we need to close the 1st
 | 
			
		||||
				 */
 | 
			
		||||
				close(sv[0]);
 | 
			
		||||
				errno = open_errno;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* set the type for the socket via ioctl
 | 
			
		||||
		 * (SOCK_STREAM, SOCK_SEQPACKET, etc)
 | 
			
		||||
		 */
 | 
			
		||||
		r= ioctl(sv[i], NWIOSUDSTYPE, &type);
 | 
			
		||||
		if (r == -1) {
 | 
			
		||||
			int ioctl_errno;
 | 
			
		||||
 | 
			
		||||
			/* if that failed rollback socket creation */
 | 
			
		||||
			ioctl_errno= errno;
 | 
			
		||||
			close(sv[i]);
 | 
			
		||||
 | 
			
		||||
			if (i == 1) {
 | 
			
		||||
				/* if we just closed the 2nd socket, we 
 | 
			
		||||
				 * need to close the 1st
 | 
			
		||||
				 */
 | 
			
		||||
				close(sv[0]);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* return the error thrown by the call to ioctl */
 | 
			
		||||
			errno= ioctl_errno;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r= fstat(sv[1], &sbuf);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		int fstat_errno;
 | 
			
		||||
 | 
			
		||||
		/* if that failed rollback socket creation */
 | 
			
		||||
		fstat_errno= errno;
 | 
			
		||||
 | 
			
		||||
		close(sv[0]);
 | 
			
		||||
		close(sv[1]);
 | 
			
		||||
 | 
			
		||||
		/* return the error thrown by the call to fstat */
 | 
			
		||||
		errno= fstat_errno;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dev = sbuf.st_dev;
 | 
			
		||||
 | 
			
		||||
	/* connect the sockets sv[0] and sv[1] */
 | 
			
		||||
	r= ioctl(sv[0], NWIOSUDSPAIR, &dev);
 | 
			
		||||
	if (r == -1) {
 | 
			
		||||
		int ioctl_errno;
 | 
			
		||||
 | 
			
		||||
		/* if that failed rollback socket creation */
 | 
			
		||||
		ioctl_errno= errno;
 | 
			
		||||
 | 
			
		||||
		close(sv[0]);
 | 
			
		||||
		close(sv[1]);
 | 
			
		||||
 | 
			
		||||
		/* return the error thrown by the call to ioctl */
 | 
			
		||||
		errno= ioctl_errno;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user