/*****************************************************************************
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 <jochen@remote.org>
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 <config.h>
#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 *****************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1