Logo Search packages:      
Sourcecode: dcc version File versions  Download package

dccif.c

/* Distributed Checksum Clearinghouse
 *
 * dccif(), a sample C interface to the DCC interface daemon, dccifd
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.19 $Revision$
 */


#include "dccif.h"
#include "dcc_clnt.h"
#include "sys/uio.h"
#include <sys/un.h>
#ifndef DCC_WIN32
#include <arpa/inet.h>
#endif



static void
dccif_closes(int *fd1, int *fd2, int *fd3)
{
      if (fd1 && *fd1 >= 0) {
            close(*fd1);
            *fd1 = -1;
      }
      if (fd2 && *fd2 >= 0) {
            close(*fd2);
            *fd2 = -1;
      }
      if (fd3 && *fd3 >= 0) {
            close(*fd3);
            *fd3 = -1;
      }
}



static void PATTRIB(6,7)
dccif_pemsg(int ex_code, DCC_EMSG emsg,
          int *fd1, int *fd2, int *fd3,
          const char *msg, ...)
{
      va_list args;

      if (emsg) {
            va_start(args, msg);
            dcc_vpemsg(ex_code, emsg, msg, args);
            va_end(args);
      }

      dccif_closes(fd1, fd2, fd3);
}



static u_char
dccif_sock(DCC_EMSG emsg,
         const char *homedir,       /* home directory or host,port */
         SOCKET *s, int* fd2, int* fd3)
{
      struct stat sb;
      char host[MAXHOSTNAMELEN];
      u_int16_t port;
      DCC_SOCKU su;
      struct sockaddr_un sunsock;
      const char *cp;
      int error;

      /* assume a non-existent home directory is a hostname,port */
      if (0 > stat(homedir, &sb)
          || !S_ISDIR(sb.st_mode)) {
            cp = dcc_parse_nm_port(emsg, homedir, DCC_GET_PORT_INVALID,
                               host, sizeof(host),
                               &port, 0, 0,
                               0, 0);
            if (!cp) {
                  dccif_closes(0, fd2, fd3);
                  return 0;
            }
            if (*cp != '\0') {
                  dccif_pemsg(EX_USAGE, emsg, 0, fd2, fd3,
                            "invalid IP address: %s",
                            homedir);
                  return 0;
            }
            if (host[0] == '\0') {
                  dccif_pemsg(EX_NOHOST, emsg, 0, fd2, fd3,
                            "missing host name in \"%s\"", homedir);
                  return 0;

            } else {
                  dcc_host_lock();
                  if (!dcc_get_host(host, 2, &error)) {
                        dcc_host_unlock();
                        dccif_pemsg(EX_NOHOST, emsg, 0, fd2, fd3,
                                  "%s: %s",
                                  homedir, hstrerror(error));
                        return 0;
                  }
                  su = dcc_hostaddrs[0];
                  *DCC_SU_PORT(&su) = port;
                  dcc_host_unlock();
            }

            *s = socket(su.sa.sa_family, SOCK_STREAM, 0);
            if (*s < 0) {
                  dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
                            "socket(AF_UNIX): %s", ERROR_STR());
                  return 0;
            }

            if (0 > connect(*s, &su.sa, DCC_SU_LEN(&su))) {
                  dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
                            "connect(%s): %s", homedir, ERROR_STR());
                  return 0;
            }
            return 1;
      }

      *s = socket(AF_UNIX, SOCK_STREAM, 0);
      if (*s < 0) {
            dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
                      "socket(AF_UNIX): %s", ERROR_STR());
            return 0;
      }
      memset(&sunsock, 0, sizeof(sunsock));
      sunsock.sun_family = AF_UNIX;
      snprintf(sunsock.sun_path, sizeof(sunsock.sun_path),
             "%s/"DCC_DCCIF_UDS, homedir);
#ifdef DCC_HAVE_SA_LEN
      sunsock.sun_len = SUN_LEN(&sunsock);
#endif
      if (0 > connect(*s, (struct sockaddr *)&sunsock, sizeof(sunsock))) {
            dccif_pemsg(EX_IOERR, emsg, s, fd2, fd3,
                      "connect(%s): %s",
                      sunsock.sun_path, ERROR_STR());
            return 0;
      }
      return 1;
}



static u_char
dccif_writev(DCC_EMSG emsg,
           int *out_fd, int *fd2, int *fd3,
           const struct iovec *iovs,
           int num_iovs,
           int wtotal)
{
      int i;

      i = writev(*out_fd, iovs, num_iovs);
      if (i == wtotal)
            return 1;
      if (i < 0)
            dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
                      "dccif writev(%d): %s", wtotal, ERROR_STR());
      else
            dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
                      "dccif writev(%d)=%d", wtotal, i);
      return 0;
}



static u_char
dccif_write(DCC_EMSG emsg,
          int *out_fd, int *fd2, int *fd3,
          const void *buf, int buf_len)
{
      int i;

      i = write(*out_fd, buf, buf_len);
      if (i == buf_len)
            return 1;
      if (i < 0)
            dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
                      "dccif writev(%d): %s", buf_len, ERROR_STR());
      else
            dccif_pemsg(EX_IOERR, emsg, out_fd, fd2, fd3,
                      "dccif writev(%d)=%d",
                      buf_len, i);
      return 0;
}



static int
dccif_read(DCC_EMSG emsg,
         int *in_fd, int *fd2, int *fd3,
         char *buf, int buf_len, int min_read)
{
      int total, i;

      total = 0;
      for (;;) {
            i = read(*in_fd, &buf[total], buf_len-total);
            if (i < 0) {
                  dccif_pemsg(EX_IOERR, emsg, in_fd, fd2, fd3,
                            "dccif read(): %s", ERROR_STR());
                  return -1;
            }
            total += i;
            if (total >= min_read)
                  return total;
            if (i == 0) {
                  dccif_pemsg(EX_IOERR, emsg, in_fd, fd2, fd3,
                            "dccif read(): premature EOF");
                  return -1;
            }
      }
}



u_char                              /* 0=failed or DCCIF_RESULT_* */
dccif(DCC_EMSG emsg,                /* put error message here */
      int out_body_fd,              /* -1 or write body to here */
      char **out_body,              /* 0 or pointer for resulting body */
      const char *opts,             /* blank separated string DCCIF_OPT_* */
      const DCC_SOCKU *clnt_addr,   /* SMTP client IPv4 or IPv6 address */
      const char *clnt_name,        /* optional SMTP client name */
      const char *helo,
      const char *env_from,         /* envelope sender */
      DCCIF_RCPT *rcpts,            /* pruned envelope recipients */
      int in_body_fd,               /* -1 or read body from here */
      const char *in_body,          /* 0 or start of incoming body */
      const char *homedir)          /* home directory or host,port */
{
      static const char nl = '\n';
      static const char cr = '\r';
      int s;
      char clnt_str[INET6_ADDRSTRLEN+1+1];
      struct iovec iovs[50], *iovp;
      int wtotal;
#     define ADD_IOV(b,l) {int _l = (l); iovp->iov_base = (char *)(b); \
            iovp->iov_len = _l; ++iovp; wtotal += _l;}
      DCCIF_RCPT *rcpt;
      char buf[4*1024];
      char result;
      int read_len, in_body_len, max_body_len, nxt;
      int i, j;

      if (!homedir || *homedir == '\0')
            homedir = DCC_HOMEDIR;

      if (emsg)
            *emsg = '\0';

      if (!clnt_addr) {
            clnt_str[0] = '\0';
      } else if (clnt_addr->sa.sa_family == AF_INET) {
            if (!DCC_INET_NTOP(AF_INET,
                           &clnt_addr->ipv4.sin_addr,
                           clnt_str, sizeof(clnt_str))) {
                  dccif_pemsg(EX_SOFTWARE, emsg,
                            0, &in_body_fd, &out_body_fd,
                            "inet_ntop(): %s", ERROR_STR());
                  return 0;
            }
      } else {
            if (!DCC_INET_NTOP(clnt_addr->sa.sa_family,
                           &clnt_addr->ipv6.sin6_addr,
                           clnt_str, sizeof(clnt_str))) {
                  dccif_pemsg(EX_SOFTWARE, emsg, 0,
                            &in_body_fd, &out_body_fd,
                            "inet_ntop(): %s", ERROR_STR());
                  return 0;
            }
      }

      if (!dccif_sock(emsg, homedir, &s, &in_body_fd, &out_body_fd))
            return 0;

      /* first line of request */
      iovp = iovs;
      wtotal = 0;
      if (opts)
            ADD_IOV(opts, strlen(opts));
      ADD_IOV(&nl, 1);

      i = strlen(clnt_str);
      clnt_str[i++] = '\r';
      ADD_IOV(clnt_str, i);
      if (i > 1 && clnt_name)
            ADD_IOV(clnt_name, strlen(clnt_name));
      ADD_IOV(&nl, 1);

      if (helo)
            ADD_IOV(helo, strlen(helo));
      ADD_IOV(&nl, 1);

      if (env_from)
            ADD_IOV(env_from, strlen(env_from));
      ADD_IOV(&nl, 1);

      for (rcpt = rcpts; rcpt; rcpt = rcpt->next) {
            ADD_IOV(rcpt->addr, strlen(rcpt->addr));
            ADD_IOV(&cr, 1);
            if (rcpt->user)
                  ADD_IOV(rcpt->user, strlen(rcpt->user));
            ADD_IOV(&nl, 1);
            if (iovp >= &iovs[DIM(iovs)-4-1]) {
                  if (!dccif_writev(emsg, &s, &in_body_fd, &out_body_fd,
                                iovs, iovp - iovs, wtotal))
                        return 0;
                  iovp = iovs;
                  wtotal = 0;
            }
      }
      ADD_IOV(&nl, 1);

      /* copy (some of) the body from the buffer to the daemon */
      if (in_body) {
            in_body_len = strlen(in_body);
            ADD_IOV(in_body, in_body_len);
      } else {
            in_body_len = 0;
      }

      if (!dccif_writev(emsg, &s, &in_body_fd, &out_body_fd,
                    iovs, iovp - iovs, wtotal))
            return 0;

      /* copy (the rest of) the body from input file to the daemon */
      if (in_body_fd >= 0) {
            for (;;) {
                  i = read(in_body_fd, buf, sizeof(buf));
                  if (i <= 0) {
                        if (i == 0)
                              break;
                        dccif_pemsg(EX_IOERR, emsg,
                                  &s, &in_body_fd, &out_body_fd,
                                  "read(body): %s", ERROR_STR());
                        return 0;
                  }
                  if (!dccif_write(emsg, &s, &in_body_fd, &out_body_fd,
                               buf, i))
                        return 0;
                  in_body_len += i;
            }
            if (0 > close(in_body_fd)) {
                  dccif_pemsg(EX_IOERR, emsg,
                            &s, &in_body_fd, &out_body_fd,
                            "close(in_body_fd): %s", ERROR_STR());
                  return 0;
            }
            in_body_fd = -1;
      }

      /* tell the daemon it has all of the body */
      if (0 > shutdown(s, 1)) {
            dccif_pemsg(EX_IOERR, emsg, &s, &in_body_fd, &out_body_fd,
                      "shutdown(): %s", ERROR_STR());
            return 0;
      }

      /* get the overall result */
      read_len = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
                        buf, sizeof(buf), 2);
      if (read_len < 0)
            return 0;
      result = buf[0];
      if (result != DCCIF_RESULT_OK
          && result != DCCIF_RESULT_GREY
          && result != DCCIF_RESULT_REJECT
          && result != DCCIF_RESULT_SOME
          && result != DCCIF_RESULT_TEMP) {
            dccif_pemsg(EX_SOFTWARE, emsg, &s, &in_body_fd, &out_body_fd,
                    "unrecognized dccifd result \"%.*s\"", 1, buf);
            return 0;
      }

      /* read the vector of results from the daemon */
      nxt = 1;                /* skip '\n' after result */
      for (rcpt = rcpts; rcpt != 0; rcpt = rcpt->next) {
            if (++nxt >= read_len) {
                  read_len = dccif_read(emsg,
                                    &s, &in_body_fd, &out_body_fd,
                                    buf, sizeof(buf), 2);
                  if (read_len < 0)
                        return 0;
                  nxt = 0;
            }
            switch (buf[nxt]) {
            case DCCIF_RCPT_ACCEPT:
            case DCCIF_RCPT_REJECT:
            case DCCIF_RCPT_GREY:
                  rcpt->ok = buf[nxt];
                  break;
            default:
                  dccif_pemsg(EX_SOFTWARE, emsg,
                            &s, &in_body_fd, &out_body_fd,
                            "unrecognized dccifd recipient result"
                            " \"%c\" for %s",
                            buf[nxt], rcpt->addr);
                  return 0;
            }
      }
      if (++nxt >= read_len) {
            read_len = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
                              buf, 1, 1);
            if (read_len < 0)
                  return 0;
            nxt = 0;
      }
      if (buf[nxt++] != '\n') {
            dccif_pemsg(EX_SOFTWARE, emsg, &s, &in_body_fd, &out_body_fd,
                    "unrecognized dccifd text after results: \"%c\"",
                    buf[nxt]);
            return 0;
      }

      /* copy the body from the daemon to the output buffer for file */
      if (out_body_fd >= 0) {
            for (;;) {
                  j = read_len - nxt;
                  if (j <= 0) {
                        j = dccif_read(emsg, &s,
                                     &in_body_fd, &out_body_fd,
                                     buf, sizeof(buf), 0);
                        if (j < 0)
                              return 0;
                        if (j == 0)
                              break;
                        read_len = j;
                        nxt = 0;
                  }
                  if (!dccif_write(emsg, &out_body_fd,
                               &s, &in_body_fd,
                               &buf[nxt], j))
                        return 0;
                  read_len = 0;
            }
            if (0 > close(out_body_fd)) {
                  dccif_pemsg(EX_IOERR, emsg,
                            &s, &in_body_fd, &out_body_fd,
                            "close(out_body_fd): %s",
                            ERROR_STR());
                  return 0;
            }
            out_body_fd = -1;

      } else if (out_body) {
            char *body;

            max_body_len = in_body_len + DCC_MAX_XHDR_LEN+1;
            body = malloc(max_body_len);
            *out_body = body;
            j = read_len - nxt;
            if (j > 0) {
                  if (j > max_body_len)
                        j = max_body_len;
                  memcpy(body, &buf[nxt], j);
                  body += j;
                  max_body_len -= j;
            }
            for (;;) {
                  if (max_body_len <= 0) {
                        dccif_pemsg(EX_SOFTWARE, emsg,
                                  &s, &in_body_fd, &out_body_fd,
                                  "too much body from dccifd");
                        free(*out_body);
                        return 0;
                  }

                  j = dccif_read(emsg, &s, &in_body_fd, &out_body_fd,
                               body, max_body_len, 0);
                  if (j < 0) {
                        free(*out_body);
                        return 0;
                  }
                  if (j == 0)
                        break;
                  body += j;
                  max_body_len -= j;
            }
            *body = '\0';
      }

      if (0 > close(s)) {
            dccif_pemsg(EX_IOERR, emsg, &s, &in_body_fd, &out_body_fd,
                      "close(socket): %s", ERROR_STR());
            return 0;
      }
      return result;
#undef ADD_IOV
}

Generated by  Doxygen 1.6.0   Back to index