diff --git a/docs/UPDATING b/docs/UPDATING index fcb9dcbcd..8fa56ea75 100644 --- a/docs/UPDATING +++ b/docs/UPDATING @@ -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 diff --git a/lib/libc/ip/Makefile.inc b/lib/libc/ip/Makefile.inc index c95e1ef71..944c62e81 100644 --- a/lib/libc/ip/Makefile.inc +++ b/lib/libc/ip/Makefile.inc @@ -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 diff --git a/lib/libc/ip/accept.c b/lib/libc/ip/accept.c index 8f1085ce7..cfbfe9357 100644 --- a/lib/libc/ip/accept.c +++ b/lib/libc/ip/accept.c @@ -1,10 +1,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -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; +} diff --git a/lib/libc/ip/bind.c b/lib/libc/ip/bind.c index f84e2be73..2749ab7b1 100644 --- a/lib/libc/ip/bind.c +++ b/lib/libc/ip/bind.c @@ -1,8 +1,14 @@ +#include +#include +#include +#include #include #include #include #include #include +#include +#include #include #include @@ -10,6 +16,10 @@ #include #include #include +#include + +#include +#include #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; +} diff --git a/lib/libc/ip/connect.c b/lib/libc/ip/connect.c index 614fee142..292df6e79 100644 --- a/lib/libc/ip/connect.c +++ b/lib/libc/ip/connect.c @@ -1,9 +1,13 @@ #include #include #include +#include +#include #include #include #include +#include +#include #include #include @@ -12,12 +16,16 @@ #include #include +#include + #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; +} diff --git a/lib/libc/ip/getpeereid.c b/lib/libc/ip/getpeereid.c new file mode 100644 index 000000000..ef7f60943 --- /dev/null +++ b/lib/libc/ip/getpeereid.c @@ -0,0 +1,35 @@ +#include +#include +#include + +/* + * 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; + } +} diff --git a/lib/libc/ip/getpeername.c b/lib/libc/ip/getpeername.c index bf6dacabb..c45c00504 100644 --- a/lib/libc/ip/getpeername.c +++ b/lib/libc/ip/getpeername.c @@ -9,20 +9,25 @@ #include #include #include +#include #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; +} diff --git a/lib/libc/ip/getsockname.c b/lib/libc/ip/getsockname.c index d3c9d7d71..acfbfbbe0 100644 --- a/lib/libc/ip/getsockname.c +++ b/lib/libc/ip/getsockname.c @@ -18,31 +18,68 @@ #include #include #include - +#include /* #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; +} diff --git a/lib/libc/ip/getsockopt.c b/lib/libc/ip/getsockopt.c index 861273628..816fb78be 100644 --- a/lib/libc/ip/getsockopt.c +++ b/lib/libc/ip/getsockopt.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,12 +14,16 @@ #include #include +#include + #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; +} diff --git a/lib/libc/ip/listen.c b/lib/libc/ip/listen.c index d2283e52e..5a8f55d03 100644 --- a/lib/libc/ip/listen.c +++ b/lib/libc/ip/listen.c @@ -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 diff --git a/lib/libc/ip/recv.c b/lib/libc/ip/recv.c index 6806ef2b2..b40f0a6a9 100644 --- a/lib/libc/ip/recv.c +++ b/lib/libc/ip/recv.c @@ -5,4 +5,3 @@ ssize_t recv(int socket, void *buffer, size_t length, int flags) { return recvfrom(socket, buffer, length, flags, NULL, NULL); } - diff --git a/lib/libc/ip/recvfrom.c b/lib/libc/ip/recvfrom.c index 659535666..8c3097c1d 100644 --- a/lib/libc/ip/recvfrom.c +++ b/lib/libc/ip/recvfrom.c @@ -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; +} + diff --git a/lib/libc/ip/recvmsg.c b/lib/libc/ip/recvmsg.c new file mode 100644 index 000000000..426c719f0 --- /dev/null +++ b/lib/libc/ip/recvmsg.c @@ -0,0 +1,95 @@ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/lib/libc/ip/send.c b/lib/libc/ip/send.c index 3e12dd90e..3bba66bf1 100644 --- a/lib/libc/ip/send.c +++ b/lib/libc/ip/send.c @@ -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); } - diff --git a/lib/libc/ip/sendmsg.c b/lib/libc/ip/sendmsg.c new file mode 100644 index 000000000..3e70951e0 --- /dev/null +++ b/lib/libc/ip/sendmsg.c @@ -0,0 +1,139 @@ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/lib/libc/ip/sendto.c b/lib/libc/ip/sendto.c index 193bf61a7..ae62a2658 100644 --- a/lib/libc/ip/sendto.c +++ b/lib/libc/ip/sendto.c @@ -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); +} diff --git a/lib/libc/ip/setsockopt.c b/lib/libc/ip/setsockopt.c index c5d362ac5..4e204a495 100644 --- a/lib/libc/ip/setsockopt.c +++ b/lib/libc/ip/setsockopt.c @@ -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; +} diff --git a/lib/libc/ip/shutdown.c b/lib/libc/ip/shutdown.c index 128ded527..b2df69be6 100644 --- a/lib/libc/ip/shutdown.c +++ b/lib/libc/ip/shutdown.c @@ -2,22 +2,25 @@ #include #include #include +#include #include #include #include -#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); +} diff --git a/lib/libc/ip/socket.c b/lib/libc/ip/socket.c index c08ded1bc..0c53aaa8d 100644 --- a/lib/libc/ip/socket.c +++ b/lib/libc/ip/socket.c @@ -6,12 +6,14 @@ #include #include +#include #include #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; +} diff --git a/lib/libc/ip/socketpair.c b/lib/libc/ip/socketpair.c new file mode 100644 index 000000000..55a2c7d73 --- /dev/null +++ b/lib/libc/ip/socketpair.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}