/***************************************************************************** POPular -- A POP3 server and proxy for large mail systems $Id: uds.c,v 1.5 2002/09/15 12:27:16 sqrt Exp $ http://www.remote.org/jochen/mail/popular/ ****************************************************************************** Copyright (C) 1999-2002 Jochen Topf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA *****************************************************************************/ #if HAVE_CONFIG_H # include #endif #include "popular.h" /***************************************************************************** =FUNCTION= uds_socket =DESCRIPTION= Open a UNIX domain datagram socket and bind it to an address. =ARGUMENTS= const char *addr address for this socket, i.e. a file name mode_t mask umask used for binding this address =RETURNS= int >= 0 file descriptor of new socket int == -1 on error (errno is set) *****************************************************************************/ int uds_socket(const char *addr, mode_t mask) { struct sockaddr_un sa; mode_t savedmask; int s; s = socket(AF_UNIX, SOCK_DGRAM, 0); if (s < 0) return -1; memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; if (strlcpy(sa.sun_path, addr, sizeof(sa.sun_path)) >= sizeof(sa.sun_path)) { errno = EINVAL; return -1; } savedmask = umask(mask); if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) return -1; (void) umask(savedmask); return s; } /***************************************************************************** =FUNCTION= uds_connect =DESCRIPTION= Connect a UNIX domain datagram socket to an address. =ARGUMENTS= int s the UNIX domain socket const char *addr the address (file name) to connect to =RETURNS= int 0 if successfull int -1 on error (errno is set) *****************************************************************************/ int uds_connect(int s, const char *addr) { struct sockaddr_un sa; memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; if (strlcpy(sa.sun_path, addr, sizeof(sa.sun_path)) >= sizeof(sa.sun_path)) { errno = EINVAL; return -1; } return connect(s, (struct sockaddr *)&sa, sizeof(sa)); } /***************************************************************************** =FUNCTION= uds_send() =DESCRIPTION= Send a message to a UNIX domain socket. If 'fd' is positive, it will be passed through the same socket. =ARGUMENTS= int s socket descriptor of UNIX domain datagram socket const char *addr address to send to. Can be NULL if s is connected char *text null-terminated string to send int fd file descriptor to be passed or -1 for no fd =RETURNS= int 0 if successfull int -1 on error, 'errno' is set accordingly *****************************************************************************/ int uds_send(int s, const char *addr, char *text, int fd) { struct sockaddr_un sa; struct iovec vector; struct msghdr msg; #ifdef HAVE_MSG_CONTROL struct cmsghdr *cmsg; #endif /* HAVE_MSG_CONTROL */ vector.iov_base = text; vector.iov_len = strlen(text)+1; memset(&msg, 0, sizeof(msg)); if (addr) { memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; if (strlcpy(sa.sun_path, addr, sizeof(sa.sun_path)) >= sizeof(sa.sun_path)) { errno = EINVAL; return -1; } msg.msg_name = &sa; msg.msg_namelen = sizeof(struct sockaddr_un); } else { msg.msg_name = NULL; msg.msg_namelen = 0; } msg.msg_iov = &vector; msg.msg_iovlen = 1; DEBUG1(DG_NET, "uds_send", "sending message: '%s'", text); if (fd > 0) { DEBUG1(DG_NET, "uds_send", "sending fd: %d", fd); #ifdef HAVE_MSG_CONTROL cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd)); if (! cmsg) return -1; cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); msg.msg_control = cmsg; msg.msg_controllen = cmsg->cmsg_len; #else /* HAVE_MSG_CONTROL */ msg.msg_accrights = (caddr_t) &fd; msg.msg_accrightslen = sizeof(int); #endif /* HAVE_MSG_CONTROL */ } if (sendmsg(s, &msg, 0) != vector.iov_len) { errno = EIO; return -1; } return 0; } /***************************************************************************** =FUNCTION= uds_recv() =DESCRIPTION= Get a message from a UNIX domain socket. If there was a file descriptor passed with the message, it is stored in 'fd', otherweise 'fd' will be left alone. =ARGUMENTS= int s socket where we read the data from char *addr address of peer socket will be stored here. Must be at least 109 bytes large. Can be NULL, if you are not interested in the address char *text buffer for storing the message. A 0-byte is always stored at the end of the buffer int size maximum length of the 'text' buffer int *fd if a file descriptor was passed, it is stored here =RETURNS= int 0 on success without attached file descriptor int 1 on success with attached file descriptor int -1 on error (errno is set) *****************************************************************************/ int uds_recv(int s, char *addr, char *text, int size, int *fd) { struct sockaddr_un sa; struct iovec vector; struct msghdr msg; struct cmsghdr *cmsg; int len; cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd)); if (!cmsg) return -1; cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd); vector.iov_base = text; vector.iov_len = size-1; memset(&msg, 0, sizeof(msg)); if (addr) { memset(&sa, 0, sizeof(sa)); msg.msg_name = &sa; msg.msg_namelen = sizeof(struct sockaddr_un); } else { msg.msg_name = NULL; msg.msg_namelen = 0; } msg.msg_iov = &vector; msg.msg_iovlen = 1; #ifdef HAVE_MSG_CONTROL msg.msg_control = cmsg; msg.msg_controllen = cmsg->cmsg_len; #else /* HAVE_MSG_CONTROL */ msg.msg_accrights = (caddr_t) fd; msg.msg_accrightslen = sizeof(int); #endif /* HAVE_MSG_CONTROL */ len = recvmsg(s, &msg, 0); if (len < 0) return -1; text[len] = 0; if (addr) { strlcpy(addr, ((struct sockaddr_un *)msg.msg_name)->sun_path, 109); DEBUG1(DG_NET, "uds_recv", "got message socket='%s'", addr); } DEBUG1(DG_NET, "uds_recv", "got message text='%s'", text); #ifdef HAVE_MSG_CONTROL if (msg.msg_controllen == sizeof(struct cmsghdr) + sizeof(fd)) { memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); #else if (msg.msg_accrightslen == sizeof(int)) { #endif /* HAVE_MSG_CONTROL */ if (*fd < 0) return 0; DEBUG1(DG_NET, "uds_recv", "got message fd=%d", *fd); return 1; } return 0; } /** THE END *****************************************************************/