--- /dev/null	2014-01-14 16:17:05.000000000 +0000
+++ new/usr/src/lib/brand/lx/lx_brand/common/socket.c	2014-01-14 16:17:05.053942294 +0000
@@ -0,0 +1,1487 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <strings.h>
+#include <alloca.h>
+#include <ucred.h>
+
+#include <sys/param.h>
+#include <sys/brand.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/igmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_socket.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+
+/*
+ * This string is used to prefix all abstract namespace unix sockets, ie all
+ * abstract namespace sockets are converted to regular sockets in the /tmp
+ * directory with .ABSK_ prefixed to their names.
+ */
+#define	ABST_PRFX "/tmp/.ABSK_"
+#define	ABST_PRFX_LEN 11
+
+static int lx_socket(ulong_t *);
+static int lx_bind(ulong_t *);
+static int lx_connect(ulong_t *);
+static int lx_listen(ulong_t *);
+static int lx_accept(ulong_t *);
+static int lx_getsockname(ulong_t *);
+static int lx_getpeername(ulong_t *);
+static int lx_socketpair(ulong_t *);
+static int lx_send(ulong_t *);
+static int lx_recv(ulong_t *);
+static int lx_sendto(ulong_t *);
+static int lx_recvfrom(ulong_t *);
+static int lx_shutdown(ulong_t *);
+static int lx_setsockopt(ulong_t *);
+static int lx_getsockopt(ulong_t *);
+static int lx_sendmsg(ulong_t *);
+static int lx_recvmsg(ulong_t *);
+
+typedef int (*sockfn_t)(ulong_t *);
+
+static struct {
+	sockfn_t s_fn;	/* Function implementing the subcommand */
+	int s_nargs;	/* Number of arguments the function takes */
+} sockfns[] = {
+	lx_socket, 3,
+	lx_bind, 3,
+	lx_connect, 3,
+	lx_listen, 2,
+	lx_accept, 3,
+	lx_getsockname, 3,
+	lx_getpeername, 3,
+	lx_socketpair, 4,
+	lx_send, 4,
+	lx_recv, 4,
+	lx_sendto, 6,
+	lx_recvfrom, 6,
+	lx_shutdown, 2,
+	lx_setsockopt, 5,
+	lx_getsockopt, 5,
+	lx_sendmsg, 3,
+	lx_recvmsg, 3
+};
+
+/*
+ * What follows are a series of tables we use to translate Linux constants
+ * into equivalent Solaris constants and back again.  I wish this were
+ * cleaner, more programmatic, and generally nicer.  Sadly, life is messy,
+ * and Unix networking even more so.
+ */
+static const int ltos_family[LX_AF_MAX + 1] =  {
+	AF_UNSPEC, AF_UNIX, AF_INET, AF_CCITT, AF_IPX,
+	AF_APPLETALK, AF_NOTSUPPORTED, AF_OSI, AF_NOTSUPPORTED,
+	AF_X25, AF_INET6, AF_CCITT, AF_DECnet,
+	AF_802, AF_POLICY, AF_KEY, AF_ROUTE,
+	AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED,
+	AF_NOTSUPPORTED, AF_SNA, AF_NOTSUPPORTED, AF_NOTSUPPORTED,
+	AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED,
+	AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED
+};
+
+#define	LTOS_FAMILY(d) ((d) <= LX_AF_MAX ? ltos_family[(d)] : AF_INVAL)
+
+static const int ltos_socktype[LX_SOCK_PACKET + 1] = {
+	SOCK_NOTSUPPORTED, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
+	SOCK_RDM, SOCK_SEQPACKET, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED,
+	SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED
+};
+
+#define	LTOS_SOCKTYPE(t)	\
+	((t) <= LX_SOCK_PACKET ? ltos_socktype[(t)] : SOCK_INVAL)
+
+/*
+ * Linux socket option type definitions
+ *
+ * The protocol `levels` are well defined (see in.h) The option values are
+ * not so well defined. Linux often uses different values to Solaris
+ * although they mean the same thing. For example, IP_TOS in Linux is
+ * defined as value 1 but in Solaris it is defined as value 3. This table
+ * maps all the Protocol levels to their options and maps them between
+ * Linux and Solaris and vice versa.  Hence the reason for the complexity.
+ */
+
+typedef struct lx_proto_opts {
+	const int *proto;	/* Linux to Solaris mapping table */
+	int maxentries;		/* max entries in this table */
+} lx_proto_opts_t;
+
+#define	OPTNOTSUP	-1	/* we don't support it */
+
+static const int ltos_ip_sockopts[LX_IP_DROP_MEMBERSHIP + 1] = {
+	OPTNOTSUP, IP_TOS, IP_TTL, IP_HDRINCL,
+	IP_OPTIONS, OPTNOTSUP, IP_RECVOPTS, IP_RETOPTS,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	IP_RECVTTL, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP,
+	IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP
+};
+
+static const int ltos_tcp_sockopts[LX_TCP_QUICKACK + 1] = {
+	OPTNOTSUP, TCP_NODELAY, TCP_MAXSEG, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	TCP_KEEPALIVE, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP
+};
+
+static const int ltos_igmp_sockopts[IGMP_MTRACE + 1] = {
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	IGMP_MINLEN, OPTNOTSUP, OPTNOTSUP, /* XXX: was IGMP_TIMER_SCALE */
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, IGMP_MEMBERSHIP_QUERY,
+	IGMP_V1_MEMBERSHIP_REPORT, IGMP_DVMRP,
+	IGMP_PIM, OPTNOTSUP, IGMP_V2_MEMBERSHIP_REPORT,
+	IGMP_V2_LEAVE_GROUP, OPTNOTSUP, OPTNOTSUP,
+	OPTNOTSUP, OPTNOTSUP, OPTNOTSUP, OPTNOTSUP,
+	IGMP_MTRACE_RESP, IGMP_MTRACE
+};
+
+static const int ltos_socket_sockopts[LX_SO_ACCEPTCONN + 1] = {
+	OPTNOTSUP,	SO_DEBUG,	SO_REUSEADDR,	SO_TYPE,
+	SO_ERROR,	SO_DONTROUTE,	SO_BROADCAST,	SO_SNDBUF,
+	SO_RCVBUF,	SO_KEEPALIVE,	SO_OOBINLINE,	OPTNOTSUP,
+	OPTNOTSUP,	SO_LINGER,	OPTNOTSUP,	OPTNOTSUP,
+	OPTNOTSUP,	OPTNOTSUP,	SO_RCVLOWAT,	SO_SNDLOWAT,
+	SO_RCVTIMEO,	SO_SNDTIMEO,	OPTNOTSUP,	OPTNOTSUP,
+	OPTNOTSUP,	OPTNOTSUP,	OPTNOTSUP,	OPTNOTSUP,
+	OPTNOTSUP,	OPTNOTSUP,	SO_ACCEPTCONN
+};
+
+#define	PROTO_SOCKOPTS(opts)    \
+	{ (opts), sizeof ((opts)) / sizeof ((opts)[0]) }
+
+/*
+ * The main Linux to Solaris protocol to options mapping table
+ * IPPROTO_TAB_SIZE can be set up to IPPROTO_MAX. All entries above
+ * IPPROTO_TAB_SIZE are in effect not implemented,
+ */
+
+#define	IPPROTO_TAB_SIZE	8
+
+static const lx_proto_opts_t ltos_proto_opts[IPPROTO_TAB_SIZE] = {
+	/* IPPROTO_IP		0 */
+	PROTO_SOCKOPTS(ltos_ip_sockopts),
+	/* SOL_SOCKET		1 */
+	PROTO_SOCKOPTS(ltos_socket_sockopts),
+	/* IPPROTO_IGMP		2 */
+	PROTO_SOCKOPTS(ltos_igmp_sockopts),
+	/* NOT IMPLEMENTED	3 */
+	{ NULL, 0 },
+	/* NOT IMPLEMENTED	4 */
+	{ NULL, 0 },
+	/* NOT IMPLEMENTED	5 */
+	{ NULL, 0 },
+	/* IPPROTO_TCP		6 */
+	PROTO_SOCKOPTS(ltos_tcp_sockopts),
+	/* NOT IMPLEMENTED	7 */
+	{ NULL, 0 }
+};
+
+/*
+ * Lifted from socket.h, since these definitions are contained within
+ * _KERNEL guards.
+ */
+#define	_CMSG_HDR_ALIGNMENT	4
+#define	_CMSG_HDR_ALIGN(x)	(((uintptr_t)(x) + _CMSG_HDR_ALIGNMENT - 1) & \
+				    ~(_CMSG_HDR_ALIGNMENT - 1))
+#define	CMSG_FIRSTHDR(m)						\
+	(((m)->msg_controllen < sizeof (struct cmsghdr)) ?		\
+	    (struct cmsghdr *)0 : (struct cmsghdr *)((m)->msg_control))
+
+#define	CMSG_NXTHDR(m, c)						\
+	(((c) == 0) ? CMSG_FIRSTHDR(m) :			\
+	((((uintptr_t)_CMSG_HDR_ALIGN((char *)(c) +			\
+	((struct cmsghdr *)(c))->cmsg_len) + sizeof (struct cmsghdr)) >	\
+	(((uintptr_t)((struct lx_msghdr *)(m))->msg_control) +		\
+	((uintptr_t)((struct lx_msghdr *)(m))->msg_controllen))) ?	\
+	((struct cmsghdr *)0) :						\
+	((struct cmsghdr *)_CMSG_HDR_ALIGN((char *)(c) +		\
+	    ((struct cmsghdr *)(c))->cmsg_len))))
+
+#define	LX_TO_SOL	1
+#define	SOL_TO_LX	2
+
+static int
+convert_cmsgs(int direction, struct lx_msghdr *msg, char *caller)
+{
+	struct cmsghdr *cmsg, *last;
+	int err = 0;
+
+	cmsg = CMSG_FIRSTHDR(msg);
+	while (cmsg != NULL && err == 0) {
+		if (direction == LX_TO_SOL) {
+			if (cmsg->cmsg_level == LX_SOL_SOCKET) {
+				cmsg->cmsg_level = SOL_SOCKET;
+				if (cmsg->cmsg_type == LX_SCM_RIGHTS)
+					cmsg->cmsg_type = SCM_RIGHTS;
+				else if (cmsg->cmsg_type == LX_SCM_CRED)
+					cmsg->cmsg_type = SCM_UCRED;
+				else
+					err = ENOTSUP;
+			} else {
+				err = ENOTSUP;
+			}
+		} else {
+			if (cmsg->cmsg_level == SOL_SOCKET) {
+				cmsg->cmsg_level = LX_SOL_SOCKET;
+				if (cmsg->cmsg_type == SCM_RIGHTS)
+					cmsg->cmsg_type = LX_SCM_RIGHTS;
+				else if (cmsg->cmsg_type == SCM_UCRED)
+					cmsg->cmsg_type = LX_SCM_CRED;
+				else
+					err = ENOTSUP;
+			} else {
+				err = ENOTSUP;
+			}
+		}
+
+		last = cmsg;
+		cmsg = CMSG_NXTHDR(msg, last);
+	}
+	if (err)
+		lx_unsupported("Unsupported socket control message in %s\n.",
+		    caller);
+
+	return (err);
+}
+
+/*
+ * If inaddr is an abstract namespace unix socket, this function expects addr
+ * to have enough memory to hold the expanded socket name, ie it must be of
+ * size *len + ABST_PRFX_LEN.
+ */
+static int
+convert_sockaddr(struct sockaddr *addr, socklen_t *len,
+	struct sockaddr *inaddr, socklen_t inlen)
+{
+	sa_family_t family;
+	int lx_in6_len;
+	int size;
+	int i, orig_len;
+
+	/*
+	 * Note that if the buffer at inaddr is ever smaller than inlen bytes,
+	 * we may erroneously return EFAULT rather than a possible EINVAL
+	 * as the copy comes before the various checks as to whether inlen
+	 * is of the proper length for the socket type.
+	 *
+	 * This isn't an issue at present because all callers to this routine
+	 * do meet that constraint.
+	 */
+	if ((ssize_t)inlen < 0)
+		return (-EINVAL);
+	if (uucopy(inaddr, addr, inlen) != 0)
+		return (-errno);
+
+	family = LTOS_FAMILY(addr->sa_family);
+
+	switch (family) {
+		case (sa_family_t)AF_NOTSUPPORTED:
+			return (-EPROTONOSUPPORT);
+		case (sa_family_t)AF_INVAL:
+			return (-EAFNOSUPPORT);
+		case AF_INET:
+			size = sizeof (struct sockaddr);
+
+			if (inlen < size)
+				return (-EINVAL);
+
+			*len = size;
+			break;
+
+		case AF_INET6:
+			/*
+			 * The Solaris sockaddr_in6 has one more 32-bit
+			 * field than the Linux version.
+			 */
+			size = sizeof (struct sockaddr_in6);
+			lx_in6_len = size - sizeof (uint32_t);
+
+			if (inlen != lx_in6_len)
+				return (-EINVAL);
+
+			*len = (sizeof (struct sockaddr_in6));
+			bzero((char *)addr + lx_in6_len, sizeof	(uint32_t));
+			break;
+
+		case AF_UNIX:
+			if (inlen > sizeof (struct sockaddr_un))
+				return (-EINVAL);
+
+			*len = inlen;
+
+			/*
+			 * Linux supports abstract unix sockets, which are
+			 * simply sockets that do not exist on the file system.
+			 * These sockets are denoted by beginning the path with
+			 * a NULL character. To support these, we strip out the
+			 * leading NULL character and change the path to point
+			 * to a real place in /tmp directory, by prepending
+			 * ABST_PRFX and replacing all illegal characters with
+			 * '_'.
+			 */
+			if (addr->sa_data[0] == '\0') {
+
+				/*
+				 * inlen is the entire size of the sockaddr_un
+				 * data structure, including the sun_family, so
+				 * we need to subtract this out. We subtract
+				 * 1 since we want to overwrite the leadin NULL
+				 * character, and thus do not include it in the
+				 * length.
+				 */
+				orig_len = inlen - sizeof (addr->sa_family) - 1;
+
+				/*
+				 * Since abstract paths can contain illegal
+				 * filename characters, we simply replace these
+				 * with '_'
+				 */
+				for (i = 1; i < orig_len + 1; i++) {
+					if (addr->sa_data[i] == '\0' ||
+					    addr->sa_data[i] == '/')
+						addr->sa_data[i] = '_';
+				}
+
+				/*
+				 * prepend ABST_PRFX to file name, minus the
+				 * leading NULL character. This places the
+				 * socket as a hidden file in the /tmp
+				 * directory.
+				 */
+				(void) memmove(addr->sa_data + ABST_PRFX_LEN,
+				    addr->sa_data + 1, orig_len);
+				bcopy(ABST_PRFX, addr->sa_data, ABST_PRFX_LEN);
+
+				/*
+				 * Since abstract socket paths may not be NULL
+				 * terminated, we must explicitly NULL terminate
+				 * our string.
+				 */
+				addr->sa_data[orig_len + ABST_PRFX_LEN] = '\0';
+
+				/*
+				 * Make len reflect the new len of our string.
+				 * Although we removed the NULL character at the
+				 * beginning of the string, we added a NULL
+				 * character to the end, so the net gain in
+				 * length is simply ABST_PRFX_LEN.
+				 */
+				*len = inlen + ABST_PRFX_LEN;
+			}
+			break;
+
+		default:
+			*len = inlen;
+	}
+
+	addr->sa_family = family;
+	return (0);
+}
+
+static int
+convert_sock_args(int in_dom, int in_type, int in_protocol, int *out_dom,
+    int *out_type)
+{
+	int domain, type;
+
+	if (in_dom < 0 || in_type < 0 || in_protocol < 0)
+		return (-EINVAL);
+
+	domain = LTOS_FAMILY(in_dom);
+	if (domain == AF_NOTSUPPORTED || domain == AF_UNSPEC)
+		return (-EAFNOSUPPORT);
+	if (domain == AF_INVAL)
+		return (-EINVAL);
+
+	type = LTOS_SOCKTYPE(in_type);
+	if (type == SOCK_NOTSUPPORTED)
+		return (-ESOCKTNOSUPPORT);
+	if (type == SOCK_INVAL)
+		return (-EINVAL);
+
+	/*
+	 * Linux does not allow the app to specify IP Protocol for raw
+	 * sockets.  Solaris does, so bail out here.
+	 */
+	if (type == SOCK_RAW && in_protocol == IPPROTO_IP)
+		return (-ESOCKTNOSUPPORT);
+
+	*out_dom = domain;
+	*out_type = type;
+	return (0);
+}
+
+static int
+convert_sockflags(int lx_flags)
+{
+	int solaris_flags = 0;
+
+	if (lx_flags & LX_MSG_OOB)
+		solaris_flags |= MSG_OOB;
+
+	if (lx_flags & LX_MSG_PEEK)
+		solaris_flags |= MSG_PEEK;
+
+	if (lx_flags & LX_MSG_DONTROUTE)
+		solaris_flags |= MSG_DONTROUTE;
+
+	if (lx_flags & LX_MSG_CTRUNC)
+		solaris_flags |= MSG_CTRUNC;
+
+	if (lx_flags & LX_MSG_TRUNC)
+		solaris_flags |= MSG_TRUNC;
+
+	if (lx_flags & LX_MSG_WAITALL)
+		solaris_flags |= MSG_WAITALL;
+
+	if (lx_flags & LX_MSG_DONTWAIT)
+		solaris_flags |= MSG_DONTWAIT;
+
+	if (lx_flags & LX_MSG_EOR)
+		solaris_flags |= MSG_EOR;
+
+	if (lx_flags & LX_MSG_PROXY)
+		lx_unsupported("socket operation with MSG_PROXY flag set");
+
+	if (lx_flags & LX_MSG_FIN)
+		lx_unsupported("socket operation with MSG_FIN flag set");
+
+	if (lx_flags & LX_MSG_SYN)
+		lx_unsupported("socket operation with MSG_SYN flag set");
+
+	if (lx_flags & LX_MSG_CONFIRM)
+		lx_unsupported("socket operation with MSG_CONFIRM set");
+
+	if (lx_flags & LX_MSG_RST)
+		lx_unsupported("socket operation with MSG_RST flag set");
+
+	if (lx_flags & LX_MSG_MORE)
+		lx_unsupported("socket operation with MSG_MORE flag set");
+
+	return (solaris_flags);
+}
+
+static int
+lx_socket(ulong_t *args)
+{
+	int domain;
+	int type;
+	int protocol = (int)args[2];
+	int fd;
+	int err;
+
+	err = convert_sock_args((int)args[0], (int)args[1], protocol,
+	    &domain, &type);
+	if (err != 0)
+		return (err);
+
+	lx_debug("\tsocket(%d, %d, %d)", domain, type, protocol);
+
+	/* Right now IPv6 sockets don't work */
+	if (domain == AF_INET6)
+		return (-EAFNOSUPPORT);
+
+	/*
+	 * Clients of the auditing subsystem used by CentOS 4 and 5 expect to
+	 * be able to create AF_ROUTE SOCK_RAW sockets to communicate with the
+	 * auditing daemons. Failure to create these sockets will cause login,
+	 * ssh and useradd, amoung other programs to fail. To trick these
+	 * programs into working, we convert the socket domain and type to
+	 * something that we do support. Then when sendto is called on these
+	 * sockets, we return an error code. See lx_sendto.
+	 */
+	if (domain == AF_ROUTE && type == SOCK_RAW) {
+		domain = AF_INET;
+		type = SOCK_STREAM;
+		protocol = 0;
+	}
+
+	fd = socket(domain, type, protocol);
+	if (fd >= 0)
+		return (fd);
+
+	if (errno == EPROTONOSUPPORT)
+		return (-ESOCKTNOSUPPORT);
+
+	return (-errno);
+}
+
+static int
+lx_bind(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct stat64 statbuf;
+	struct sockaddr *name, oldname;
+	socklen_t len;
+	int r, r2, ret, tmperrno;
+	int abst_sock;
+	struct stat sb;
+
+	if (uucopy((struct sockaddr *)args[1], &oldname,
+	    sizeof (struct sockaddr)) != 0)
+		return (-errno);
+
+	/*
+	 * Handle Linux abstract sockets, which are UNIX sockets whose path
+	 * begins with a NULL character.
+	 */
+	abst_sock = (oldname.sa_family == AF_UNIX) &&
+	    (oldname.sa_data[0] == '\0');
+
+	/*
+	 * convert_sockaddr will expand the socket path if it is abstract, so
+	 * we need to allocate extra memory for it now.
+	 */
+	if ((name = SAFE_ALLOCA((socklen_t)args[2] +
+	    abst_sock * ABST_PRFX_LEN)) == NULL)
+		return (-EINVAL);
+
+	if ((r = convert_sockaddr(name, &len, (struct sockaddr *)args[1],
+	    (socklen_t)args[2])) < 0)
+		return (r);
+
+	/*
+	 * Linux abstract namespace unix sockets are simply socket that do not
+	 * exist on the filesystem. We emulate them by changing their paths
+	 * in covert_sockaddr so that they point real files names on the
+	 * filesystem. Because in Linux they do not exist on the filesystem
+	 * applications do not have to worry about deleting files, however in
+	 * our filesystem based emulation we do. To solve this problem, we first
+	 * check to see if the socket already exists before we create one. If it
+	 * does we attempt to connect to it to see if it is in use, or just
+	 * left over from a previous lx_bind call. If we are unable to connect,
+	 * we assume it is not in use and remove the file, then continue on
+	 * as if the file never existed.
+	 */
+	if (abst_sock && stat(name->sa_data, &sb) == 0 &&
+	    S_ISSOCK(sb.st_mode)) {
+		if ((r2 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+			return (-ENOSR);
+		ret = connect(r2, name, len);
+		tmperrno = errno;
+		if (close(r2) < 0)
+			return (-EINVAL);
+
+		/*
+		 * if we can't connect to the socket, assume no one is using it
+		 * and remove it, otherwise assume it is in use and return
+		 * EADDRINUSE.
+		 */
+		if ((ret < 0) && (tmperrno == ECONNREFUSED)) {
+			if (unlink(name->sa_data) < 0) {
+				return (-EADDRINUSE);
+			}
+		} else {
+			return (-EADDRINUSE);
+		}
+	}
+
+	lx_debug("\tbind(%d, 0x%p, %d)", sockfd, name, len);
+
+	if (name->sa_family == AF_UNIX)
+		lx_debug("\t\tAF_UNIX, path = %s", name->sa_data);
+
+	r = bind(sockfd, name, len);
+
+	/*
+	 * Linux returns EADDRINUSE for attempts to bind to UNIX domain
+	 * sockets that aren't sockets.
+	 */
+	if ((r < 0) && (errno == EINVAL) && (name->sa_family == AF_UNIX) &&
+	    ((stat64(name->sa_data, &statbuf) == 0) &&
+	    (!S_ISSOCK(statbuf.st_mode))))
+		return (-EADDRINUSE);
+
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_connect(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct sockaddr *name, oldname;
+	socklen_t len;
+	int r;
+	int abst_sock;
+
+	if (uucopy((struct sockaddr *)args[1], &oldname,
+	    sizeof (struct sockaddr)) != 0)
+		return (-errno);
+
+
+	/* Handle Linux abstract sockets */
+	abst_sock = (oldname.sa_family == AF_UNIX) &&
+	    (oldname.sa_data[0] == '\0');
+
+	/*
+	 * convert_sockaddr will expand the socket path, if it is abstract, so
+	 * we need to allocate extra memory for it now.
+	 */
+	if ((name = SAFE_ALLOCA((socklen_t)args[2] +
+	    abst_sock * ABST_PRFX_LEN)) == NULL)
+		return (-EINVAL);
+
+	if ((r = convert_sockaddr(name, &len, (struct sockaddr *)args[1],
+	    (socklen_t)args[2])) < 0)
+		return (r);
+
+	lx_debug("\tconnect(%d, 0x%p, %d)", sockfd, name, len);
+
+	if (name->sa_family == AF_UNIX)
+		lx_debug("\t\tAF_UNIX, path = %s", name->sa_data);
+
+	r = connect(sockfd, name, len);
+
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_listen(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	int backlog = (int)args[1];
+	int r;
+
+	lx_debug("\tlisten(%d, %d)", sockfd, backlog);
+	r = listen(sockfd, backlog);
+
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_accept(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct sockaddr *name = (struct sockaddr *)args[1];
+	socklen_t namelen = 0;
+	int r;
+
+	lx_debug("\taccept(%d, 0x%p, 0x%p", sockfd, args[1], args[2]);
+
+	/*
+	 * The Linux man page says that -1 is returned and errno is set to
+	 * EFAULT if the "name" address is bad, but it is silent on what to
+	 * set errno to if the "namelen" address is bad.  Experimentation
+	 * shows that Linux (at least the 2.4.21 kernel in CentOS) actually
+	 * sets errno to EINVAL in both cases.
+	 *
+	 * Note that we must first check the name pointer, as the Linux
+	 * docs state nothing is copied out if the "name" pointer is NULL.
+	 * If it is NULL, we don't care about the namelen pointer's value
+	 * or about dereferencing it.
+	 *
+	 * Happily, Solaris' accept(3SOCKET) treats NULL name pointers and
+	 * zero namelens the same way.
+	 */
+	if ((name != NULL) &&
+	    (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0))
+		return ((errno == EFAULT) ? -EINVAL : -errno);
+
+	lx_debug("\taccept namelen = %d", namelen);
+
+	if ((r = accept(sockfd, name, &namelen)) < 0)
+		return ((errno == EFAULT) ? -EINVAL : -errno);
+
+	lx_debug("\taccept namelen returned %d bytes", namelen);
+
+	/*
+	 * In Linux, accept()ed sockets do not inherit anything set by
+	 * fcntl(), so filter those out.
+	 */
+	if (fcntl(r, F_SETFL, 0) < 0)
+		return (-errno);
+
+	/*
+	 * Once again, a bad "namelen" address sets errno to EINVAL, not
+	 * EFAULT.  If namelen was zero, there's no need to copy a zero back
+	 * out.
+	 *
+	 * Logic might dictate that we should check if we can write to
+	 * the namelen pointer earlier so we don't accept a pending connection
+	 * only to fail the call because we can't write the namelen value back
+	 * out. However, testing shows Linux does indeed fail the call after
+	 * accepting the connection so we must behave in a compatible manner.
+	 */
+	if ((name != NULL) && (namelen != 0) &&
+	    (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0))
+		return ((errno == EFAULT) ? -EINVAL : -errno);
+
+	return (r);
+}
+
+static int
+lx_getsockname(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct sockaddr *name = NULL;
+	socklen_t namelen, namelen_orig;
+
+	if (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0)
+		return (-errno);
+	namelen_orig = namelen;
+
+	lx_debug("\tgetsockname(%d, 0x%p, 0x%p (=%d))",
+	    sockfd, args[1], args[2], namelen);
+
+	if (namelen > 0) {
+		if ((name = SAFE_ALLOCA(namelen)) == NULL)
+			return (-EINVAL);
+		bzero(name, namelen);
+	}
+
+	if ((getsockname(sockfd, name, &namelen)) < 0)
+		return (-errno);
+
+	/*
+	 * If the name that getsockname() want's to return is larger
+	 * than namelen, getsockname() will copy out the maximum amount
+	 * of data possible and then update namelen to indicate the
+	 * actually size of all the data that it wanted to copy out.
+	 */
+	if (uucopy(name, (void *)args[1], namelen_orig) != 0)
+		return (-errno);
+	if (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0)
+		return (-errno);
+
+	return (0);
+}
+
+static int
+lx_getpeername(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct sockaddr *name;
+	socklen_t namelen;
+
+	if (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0)
+		return (-errno);
+
+	lx_debug("\tgetpeername(%d, 0x%p, 0x%p (=%d))",
+	    sockfd, args[1], args[2], namelen);
+
+	/*
+	 * Linux returns EFAULT in this case, even if the namelen parameter
+	 * is 0.  This check will not catch other illegal addresses, but
+	 * the benefit catching a non-null illegal address here is not
+	 * worth the cost of another system call.
+	 */
+	if ((void *)args[1] == NULL)
+		return (-EFAULT);
+
+	if ((name = SAFE_ALLOCA(namelen)) == NULL)
+		return (-EINVAL);
+	if ((getpeername(sockfd, name, &namelen)) < 0)
+		return (-errno);
+
+	if (uucopy(name, (void *)args[1], namelen) != 0)
+		return (-errno);
+
+	if (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0)
+		return (-errno);
+
+	return (0);
+}
+
+static int
+lx_socketpair(ulong_t *args)
+{
+	int domain;
+	int type;
+	int protocol = (int)args[2];
+	int *sv = (int *)args[3];
+	int fds[2];
+	int r;
+
+	r = convert_sock_args((int)args[0], (int)args[1], protocol,
+	    &domain, &type);
+	if (r != 0)
+		return (r);
+
+	lx_debug("\tsocketpair(%d, %d, %d, 0x%p)", domain, type, protocol, sv);
+
+	r = socketpair(domain, type, protocol, fds);
+
+	if (r == 0) {
+		if (uucopy(fds, sv, sizeof (fds)) != 0) {
+			r = errno;
+			(void) close(fds[0]);
+			(void) close(fds[1]);
+			return (-r);
+		}
+		return (0);
+	}
+
+	if (errno == EPROTONOSUPPORT)
+		return (-ESOCKTNOSUPPORT);
+
+	return (-errno);
+}
+
+static ssize_t
+lx_send(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	void *buf = (void *)args[1];
+	size_t len = (size_t)args[2];
+	int flags = (int)args[3];
+	ssize_t r;
+
+	int nosigpipe = flags & LX_MSG_NOSIGNAL;
+	struct sigaction newact, oact;
+
+	lx_debug("\tsend(%d, 0x%p, 0x%d, 0x%x)", sockfd, buf, len, flags);
+
+	flags = convert_sockflags(flags);
+
+	/*
+	 * If nosigpipe is set, we want to emulate the Linux action of
+	 * not sending a SIGPIPE to the caller if the remote socket has
+	 * already been closed.
+	 *
+	 * As SIGPIPE is a directed signal sent only to the thread that
+	 * performed the action, we can emulate this behavior by momentarily
+	 * resetting the action for SIGPIPE to SIG_IGN, performing the socket
+	 * call, and resetting the action back to its previous value.
+	 */
+	if (nosigpipe) {
+		newact.sa_handler = SIG_IGN;
+		newact.sa_flags = 0;
+		(void) sigemptyset(&newact.sa_mask);
+
+		if (sigaction(SIGPIPE, &newact, &oact) < 0)
+			lx_err_fatal(gettext(
+			    "%s: could not ignore SIGPIPE to emulate "
+			    "LX_MSG_NOSIGNAL"), "send()");
+	}
+
+	r = send(sockfd, buf, len, flags);
+
+	if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0))
+		lx_err_fatal(
+		    gettext("%s: could not reset SIGPIPE handler to "
+		    "emulate LX_MSG_NOSIGNAL"), "send()");
+
+	return ((r < 0) ? -errno : r);
+}
+
+static ssize_t
+lx_recv(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	void *buf = (void *)args[1];
+	size_t len = (size_t)args[2];
+	int flags = (int)args[3];
+	ssize_t r;
+
+	int nosigpipe = flags & LX_MSG_NOSIGNAL;
+	struct sigaction newact, oact;
+
+	lx_debug("\trecv(%d, 0x%p, 0x%d, 0x%x)", sockfd, buf, len, flags);
+
+	flags = convert_sockflags(flags);
+
+	/*
+	 * If nosigpipe is set, we want to emulate the Linux action of
+	 * not sending a SIGPIPE to the caller if the remote socket has
+	 * already been closed.
+	 *
+	 * As SIGPIPE is a directed signal sent only to the thread that
+	 * performed the action, we can emulate this behavior by momentarily
+	 * resetting the action for SIGPIPE to SIG_IGN, performing the socket
+	 * call, and resetting the action back to its previous value.
+	 */
+	if (nosigpipe) {
+		newact.sa_handler = SIG_IGN;
+		newact.sa_flags = 0;
+		(void) sigemptyset(&newact.sa_mask);
+
+		if (sigaction(SIGPIPE, &newact, &oact) < 0)
+			lx_err_fatal(gettext(
+			    "%s: could not ignore SIGPIPE to emulate "
+			    "LX_MSG_NOSIGNAL"), "recv()");
+	}
+
+	r = recv(sockfd, buf, len, flags);
+
+	if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0))
+		lx_err_fatal(
+		    gettext("%s: could not reset SIGPIPE handler to "
+		    "emulate LX_MSG_NOSIGNAL"), "recv()");
+
+	return ((r < 0) ? -errno : r);
+}
+
+static ssize_t
+lx_sendto(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	void *buf = (void *)args[1];
+	size_t len = (size_t)args[2];
+	int flags = (int)args[3];
+	struct sockaddr *to = NULL, oldto;
+	socklen_t tolen = 0;
+	ssize_t r;
+	int abst_sock;
+
+	int nosigpipe = flags & LX_MSG_NOSIGNAL;
+	struct sigaction newact, oact;
+
+	if ((args[4] != NULL) && (args[5] > 0)) {
+		if (uucopy((struct sockaddr *)args[4], &oldto,
+		    sizeof (struct sockaddr)) != 0)
+			return (-errno);
+
+		/* Handle Linux abstract sockets */
+		abst_sock = (oldto.sa_family == AF_UNIX) &&
+		    (oldto.sa_data[0] == '\0');
+
+		/*
+		 * convert_sockaddr will expand the socket path, if it is
+		 * abstract, so we need to allocate extra memory for it now.
+		 */
+		if ((to = SAFE_ALLOCA(args[5] + abst_sock * ABST_PRFX_LEN))
+		    == NULL)
+			return (-EINVAL);
+
+		if ((r = convert_sockaddr(to, &tolen,
+		    (struct sockaddr *)args[4], (socklen_t)args[5])) < 0)
+			return (r);
+	}
+
+
+	lx_debug("\tsendto(%d, 0x%p, 0x%d, 0x%x, 0x%x, %d)", sockfd, buf, len,
+	    flags, to, tolen);
+
+	flags = convert_sockflags(flags);
+
+	/* return this error to make auditing subsystem happy */
+	if (to && to->sa_family == AF_ROUTE) {
+		return (-ECONNREFUSED);
+	}
+
+	/*
+	 * If nosigpipe is set, we want to emulate the Linux action of
+	 * not sending a SIGPIPE to the caller if the remote socket has
+	 * already been closed.
+	 *
+	 * As SIGPIPE is a directed signal sent only to the thread that
+	 * performed the action, we can emulate this behavior by momentarily
+	 * resetting the action for SIGPIPE to SIG_IGN, performing the socket
+	 * call, and resetting the action back to its previous value.
+	 */
+	if (nosigpipe) {
+		newact.sa_handler = SIG_IGN;
+		newact.sa_flags = 0;
+		(void) sigemptyset(&newact.sa_mask);
+
+		if (sigaction(SIGPIPE, &newact, &oact) < 0)
+			lx_err_fatal(gettext(
+			    "%s: could not ignore SIGPIPE to emulate "
+			    "LX_MSG_NOSIGNAL"), "sendto()");
+	}
+
+	r = sendto(sockfd, buf, len, flags, to, tolen);
+
+	if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0))
+		lx_err_fatal(
+		    gettext("%s: could not reset SIGPIPE handler to "
+		    "emulate LX_MSG_NOSIGNAL"), "sendto()");
+
+	if (r < 0) {
+		/*
+		 * according to the man page and LTP, the expected error in
+		 * this case is EPIPE.
+		 */
+		if (errno == ENOTCONN)
+			return (-EPIPE);
+		else
+			return (-errno);
+	}
+	return (r);
+}
+
+static ssize_t
+lx_recvfrom(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	void *buf = (void *)args[1];
+	size_t len = (size_t)args[2];
+	int flags = (int)args[3];
+	struct sockaddr *from = (struct sockaddr *)args[4];
+	socklen_t *from_lenp = (socklen_t *)args[5];
+	ssize_t r;
+
+	int nosigpipe = flags & LX_MSG_NOSIGNAL;
+	struct sigaction newact, oact;
+
+	lx_debug("\trecvfrom(%d, 0x%p, 0x%d, 0x%x, 0x%x, 0x%p)", sockfd, buf,
+	    len, flags, from, from_lenp);
+
+	flags = convert_sockflags(flags);
+
+	/*
+	 * If nosigpipe is set, we want to emulate the Linux action of
+	 * not sending a SIGPIPE to the caller if the remote socket has
+	 * already been closed.
+	 *
+	 * As SIGPIPE is a directed signal sent only to the thread that
+	 * performed the action, we can emulate this behavior by momentarily
+	 * resetting the action for SIGPIPE to SIG_IGN, performing the socket
+	 * call, and resetting the action back to its previous value.
+	 */
+	if (nosigpipe) {
+		newact.sa_handler = SIG_IGN;
+		newact.sa_flags = 0;
+		(void) sigemptyset(&newact.sa_mask);
+
+		if (sigaction(SIGPIPE, &newact, &oact) < 0)
+			lx_err_fatal(gettext(
+			    "%s: could not ignore SIGPIPE to emulate "
+			    "LX_MSG_NOSIGNAL"), "recvfrom()");
+	}
+
+	r = recvfrom(sockfd, buf, len, flags, from, from_lenp);
+
+	if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0))
+		lx_err_fatal(
+		    gettext("%s: could not reset SIGPIPE handler to "
+		    "emulate LX_MSG_NOSIGNAL"), "recvfrom()");
+
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_shutdown(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	int how = (int)args[1];
+	int r;
+
+	lx_debug("\tshutdown(%d, %d)", sockfd, how);
+	r = shutdown(sockfd, how);
+
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_setsockopt(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	int level = (int)args[1];
+	int optname = (int)args[2];
+	void *optval = (void *)args[3];
+	int optlen = (int)args[4];
+	int internal_opt;
+	int r;
+
+	lx_debug("\tsetsockopt(%d, %d, %d, 0x%p, %d)", sockfd, level, optname,
+	    optval, optlen);
+
+	/*
+	 * The kernel returns EFAULT for all invalid addresses except NULL,
+	 * for which it returns EINVAL.  Linux wants EFAULT for NULL too.
+	 */
+	if (optval == NULL)
+		return (-EFAULT);
+
+	/*
+	 * Do a table lookup of the Solaris equivalent of the given option
+	 */
+	if (level < IPPROTO_IP || level >= IPPROTO_TAB_SIZE)
+		return (-ENOPROTOOPT);
+
+	if (ltos_proto_opts[level].maxentries == 0 ||
+	    optname <= 0 || optname >= (ltos_proto_opts[level].maxentries))
+		return (-ENOPROTOOPT);
+
+	/*
+	 * Linux sets this option when it wants to send credentials over a
+	 * socket. Currently we just ignore it to make Linux programs happy.
+	 */
+	if ((level == LX_SOL_SOCKET) && (optname == LX_SO_PASSCRED))
+		return (0);
+
+
+	if ((level == IPPROTO_TCP) && (optname == LX_TCP_CORK)) {
+		/*
+		 * TCP_CORK is a Linux-only option that instructs the TCP
+		 * stack not to send out partial frames.  Solaris doesn't
+		 * include this option but some apps require it.  So, we do
+		 * our best to emulate the option by disabling TCP_NODELAY.
+		 * If the app requests that we disable TCP_CORK, we just
+		 * ignore it since enabling TCP_NODELAY may be
+		 * overcompensating.
+		 */
+		optname = TCP_NODELAY;
+		if (optlen != sizeof (int))
+			return (-EINVAL);
+		if (uucopy(optval, &internal_opt, sizeof (int)) != 0)
+			return (-errno);
+		if (internal_opt == 0)
+			return (0);
+		internal_opt = 1;
+		optval = &internal_opt;
+	} else {
+		optname = ltos_proto_opts[level].proto[optname];
+
+		if (optname == OPTNOTSUP)
+			return (-ENOPROTOOPT);
+	}
+
+	if (level == LX_SOL_SOCKET)
+		level = SOL_SOCKET;
+
+	r = setsockopt(sockfd, level, optname, optval, optlen);
+
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_getsockopt(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	int level = (int)args[1];
+	int optname = (int)args[2];
+	void *optval = (void *)args[3];
+	int *optlenp = (int *)args[4];
+	int r;
+
+	lx_debug("\tgetsockopt(%d, %d, %d, 0x%p, 0x%p)", sockfd, level, optname,
+	    optval, optlenp);
+
+	/*
+	 * According to the Linux man page, a NULL optval should indicate
+	 * (as in Solaris) that no return value is expected.  Instead, it
+	 * actually triggers an EFAULT error.
+	 */
+	if (optval == NULL)
+		return (-EFAULT);
+
+	/*
+	 * Do a table lookup of the Solaris equivalent of the given option
+	 */
+	if (level < IPPROTO_IP || level >= IPPROTO_TAB_SIZE)
+		return (-EOPNOTSUPP);
+
+	if (ltos_proto_opts[level].maxentries == 0 ||
+	    optname <= 0 || optname >= (ltos_proto_opts[level].maxentries))
+		return (-ENOPROTOOPT);
+
+	if (((level == LX_SOL_SOCKET) && (optname == LX_SO_PASSCRED)) ||
+	    ((level == IPPROTO_TCP) && (optname == LX_TCP_CORK))) {
+		/*
+		 * Linux sets LX_SO_PASSCRED when it wants to send credentials
+		 * over a socket. Since we do not support it, it is never set
+		 * and we return 0.
+		 *
+		 * We don't support TCP_CORK but some apps rely on it.  So,
+		 * rather than return an error we just return 0.  This
+		 * isn't exactly a lie, since this option really isn't set,
+		 * but it's not the whole truth either.  Fortunately, we
+		 * aren't under oath.
+		 */
+		r = 0;
+		if (uucopy(&r, optval, sizeof (int)) != 0)
+			return (-errno);
+		r = sizeof (int);
+		if (uucopy(&r, optlenp, sizeof (int)) != 0)
+			return (-errno);
+		return (0);
+	}
+	if ((level == LX_SOL_SOCKET) && (optname == LX_SO_PEERCRED)) {
+		struct lx_ucred	lx_ucred;
+		ucred_t		*ucp;
+
+		/*
+		 * We don't support SO_PEERCRED, but we do have equivalent
+		 * functionality in getpeerucred() so invoke that here.
+		 */
+
+		/* Verify there's going to be enough room for the results. */
+		if (uucopy(optlenp, &r, sizeof (int)) != 0)
+			return (-errno);
+		if (r < sizeof (struct lx_ucred))
+			return (-EOVERFLOW);
+
+		/*
+		 * We allocate a ucred_t ourselves rather than allow
+		 * getpeerucred() to do it for us because getpeerucred()
+		 * uses malloc(3C) and we'd rather use SAFE_ALLOCA().
+		 */
+		if ((ucp = (ucred_t *)SAFE_ALLOCA(ucred_size())) == NULL)
+			return (-ENOMEM);
+
+		/* Get the credential for the remote end of this socket. */
+		if (getpeerucred(sockfd, &ucp) != 0)
+			return (-errno);
+		if (((lx_ucred.lxu_pid = ucred_getpid(ucp)) == -1) ||
+		    ((lx_ucred.lxu_uid = ucred_geteuid(ucp)) == (uid_t)-1) ||
+		    ((lx_ucred.lxu_gid = ucred_getegid(ucp)) == (gid_t)-1)) {
+			return (-errno);
+		}
+
+		/* Copy out the results. */
+		if ((uucopy(&lx_ucred, optval, sizeof (lx_ucred))) != 0)
+			return (-errno);
+		r = sizeof (lx_ucred);
+		if ((uucopy(&r, optlenp, sizeof (int))) != 0)
+			return (-errno);
+		return (0);
+	}
+
+	optname = ltos_proto_opts[level].proto[optname];
+
+	if (optname == OPTNOTSUP)
+		return (-ENOPROTOOPT);
+
+	if (level == LX_SOL_SOCKET)
+		level = SOL_SOCKET;
+
+	r = getsockopt(sockfd, level, optname, optval, optlenp);
+
+	return ((r < 0) ? -errno : r);
+}
+
+/*
+ * libc routines that issue these system calls.  We bypass the libsocket
+ * wrappers since they explicitly turn off the MSG_XPG_2 flag we need for
+ * Linux compatibility.
+ */
+extern int _so_sendmsg();
+extern int _so_recvmsg();
+
+static int
+lx_sendmsg(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct lx_msghdr msg;
+	struct cmsghdr *cmsg;
+	int flags = (int)args[2];
+	int r;
+
+	int nosigpipe = flags & LX_MSG_NOSIGNAL;
+	struct sigaction newact, oact;
+
+	lx_debug("\tsendmsg(%d, 0x%p, 0x%x)", sockfd, (void *)args[1], flags);
+
+	flags = convert_sockflags(flags);
+
+	if ((uucopy((void *)args[1], &msg, sizeof (msg))) != 0)
+		return (-errno);
+
+	/*
+	 * If there are control messages bundled in this message, we need
+	 * to convert them from Linux to Solaris.
+	 */
+	if (msg.msg_control != NULL) {
+		if (msg.msg_controllen == 0) {
+			cmsg = NULL;
+		} else {
+			cmsg = SAFE_ALLOCA(msg.msg_controllen);
+			if (cmsg == NULL)
+				return (-EINVAL);
+		}
+		if ((uucopy(msg.msg_control, cmsg, msg.msg_controllen)) != 0)
+			return (-errno);
+		msg.msg_control = cmsg;
+		if ((r = convert_cmsgs(LX_TO_SOL, &msg, "sendmsg()")) != 0)
+			return (-r);
+	}
+
+	/*
+	 * If nosigpipe is set, we want to emulate the Linux action of
+	 * not sending a SIGPIPE to the caller if the remote socket has
+	 * already been closed.
+	 *
+	 * As SIGPIPE is a directed signal sent only to the thread that
+	 * performed the action, we can emulate this behavior by momentarily
+	 * resetting the action for SIGPIPE to SIG_IGN, performing the socket
+	 * call, and resetting the action back to its previous value.
+	 */
+	if (nosigpipe) {
+		newact.sa_handler = SIG_IGN;
+		newact.sa_flags = 0;
+		(void) sigemptyset(&newact.sa_mask);
+
+		if (sigaction(SIGPIPE, &newact, &oact) < 0)
+			lx_err_fatal(gettext(
+			    "%s: could not ignore SIGPIPE to emulate "
+			    "LX_MSG_NOSIGNAL"), "sendmsg()");
+	}
+
+	r = _so_sendmsg(sockfd, (struct msghdr *)&msg, flags | MSG_XPG4_2);
+
+	if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0))
+		lx_err_fatal(
+		    gettext("%s: could not reset SIGPIPE handler to "
+		    "emulate LX_MSG_NOSIGNAL"), "sendmsg()");
+
+	if (r < 0) {
+		/*
+		 * according to the man page and LTP, the expected error in
+		 * this case is EPIPE.
+		 */
+		if (errno == ENOTCONN)
+			return (-EPIPE);
+		else
+			return (-errno);
+	}
+
+	return (r);
+}
+
+static int
+lx_recvmsg(ulong_t *args)
+{
+	int sockfd = (int)args[0];
+	struct lx_msghdr msg;
+	struct lx_msghdr *msgp = (struct lx_msghdr *)args[1];
+	struct cmsghdr *cmsg = NULL;
+	int flags = (int)args[2];
+	int r, err;
+
+	int nosigpipe = flags & LX_MSG_NOSIGNAL;
+	struct sigaction newact, oact;
+
+	lx_debug("\trecvmsg(%d, 0x%p, 0x%x)", sockfd, (void *)args[1], flags);
+
+	flags = convert_sockflags(flags);
+
+	if ((uucopy(msgp, &msg, sizeof (msg))) != 0)
+		return (-errno);
+
+	/*
+	 * If we are expecting to have to convert any control messages,
+	 * then we should receive them into our address space instead of
+	 * the app's.
+	 */
+	if (msg.msg_control != NULL) {
+		cmsg = msg.msg_control;
+		if (msg.msg_controllen == 0) {
+			msg.msg_control = NULL;
+		} else {
+			msg.msg_control = SAFE_ALLOCA(msg.msg_controllen);
+			if (msg.msg_control == NULL)
+				return (-EINVAL);
+		}
+	}
+
+	/*
+	 * If nosigpipe is set, we want to emulate the Linux action of
+	 * not sending a SIGPIPE to the caller if the remote socket has
+	 * already been closed.
+	 *
+	 * As SIGPIPE is a directed signal sent only to the thread that
+	 * performed the action, we can emulate this behavior by momentarily
+	 * resetting the action for SIGPIPE to SIG_IGN, performing the socket
+	 * call, and resetting the action back to its previous value.
+	 */
+	if (nosigpipe) {
+		newact.sa_handler = SIG_IGN;
+		newact.sa_flags = 0;
+		(void) sigemptyset(&newact.sa_mask);
+
+		if (sigaction(SIGPIPE, &newact, &oact) < 0)
+			lx_err_fatal(gettext(
+			    "%s: could not ignore SIGPIPE to emulate "
+			    "LX_MSG_NOSIGNAL"), "recvmsg()");
+	}
+
+	r = _so_recvmsg(sockfd, (struct msghdr *)&msg, flags | MSG_XPG4_2);
+
+	if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0))
+		lx_err_fatal(
+		    gettext("%s: could not reset SIGPIPE handler to "
+		    "emulate LX_MSG_NOSIGNAL"), "recvmsg()");
+
+	if (r >= 0 && msg.msg_control != NULL) {
+		/*
+		 * If there are control messages bundled in this message,
+		 * we need to convert them from Linux to Solaris.
+		 */
+		if ((err = convert_cmsgs(SOL_TO_LX, &msg, "recvmsg()")) != 0)
+			return (-err);
+
+		if ((uucopy(msg.msg_control, cmsg, msg.msg_controllen)) != 0)
+			return (-errno);
+	}
+
+	/*
+	 * A handful of the values in the msghdr are set by the recvmsg()
+	 * call, so copy their values back to the caller.  Rather than iterate,
+	 * just copy the whole structure back.
+	 */
+	if (uucopy(&msg, msgp, sizeof (msg)) != 0)
+		return (-errno);
+
+	return ((r < 0) ? -errno : r);
+}
+
+int
+lx_socketcall(uintptr_t p1, uintptr_t p2)
+{
+	int subcmd = (int)p1 - 1; /* subcommands start at 1 - not 0 */
+	ulong_t args[6];
+	int r;
+
+	if (subcmd < 0 || subcmd >= LX_RECVMSG)
+		return (-EINVAL);
+
+	/*
+	 * Copy the arguments to the subcommand in from the app's address
+	 * space, returning EFAULT if we get a bogus pointer.
+	 */
+	if (uucopy((void *)p2, args,
+	    sockfns[subcmd].s_nargs * sizeof (ulong_t)))
+		return (-errno);
+
+	r = (sockfns[subcmd].s_fn)(args);
+
+	return (r);
+}
