/*****************************************************************************

  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