Esempio n. 1
0
/* read in the headers */
void header_load(FILE *f)
{
    char line[1024];

    header_clear();

    while (1) {
        char *p;

        if (!fgets_strip(line, sizeof(line)-1, f)) {
            errmsg("Failed to read header %d\n", num_headers);
            exit_cleanup(1);
        }

        /* it may be the end of the headers */
        if (line[0] == 0) break;

        p = strchr(line,':');
        if (!p) {
            errmsg("no colon in header?!? [%s]\n", line);
            exit_cleanup(1);
        }

        *p++ = 0;
        while (*p && isspace(*p)) p++;

        header_add(line, p);
    }
}
Esempio n. 2
0
/*
 * Add a header line, will automatically tack on a '\n' if necessary
 */
static PyObject *expy_header_add(PyObject *self, PyObject *args)
    {
    char *str;

    if (!PyArg_ParseTuple(args, "s", &str))
        return NULL;
 
    header_add(' ', get_format_string(str, 1));

    Py_INCREF(Py_None);
    return Py_None;
    }
Esempio n. 3
0
/* add a element to a header list */
void header_add_list(char *label, char *content)
{
    struct header *h = header_find(label);
    char *p;

    if (!h) {
        header_add(label, content);
        return;
    }

    p = (char *)xmalloc(strlen(h->content) + strlen(content) + 3);
    strcpy(p, h->content);
    strcat(p, ", ");
    strcat(p, content);

    free(h->content);
    h->content = p;
}
Esempio n. 4
0
int dcc_process(uschar **listptr) {
  int sep = 0;
  uschar *list = *listptr;
  FILE *data_file;
  uschar *dcc_daemon_ip = US"";
  uschar *dcc_default_ip_option = US"127.0.0.1";
  uschar *dcc_ip_option = US"";
  uschar *dcc_helo_option = US"localhost";
  uschar *dcc_reject_message = US"Rejected by DCC";
  uschar *xtra_hdrs = NULL;

  /* from local_scan */
  int i, j, k, c, retval, sockfd, resp, line;
  unsigned int portnr;
  struct sockaddr_un  serv_addr;
  struct sockaddr_in  serv_addr_in;
  struct hostent *ipaddress;
  uschar sockpath[128];
  uschar sockip[40], client_ip[40];
  uschar opts[128];
  uschar rcpt[128], from[128];
  uschar sendbuf[4096];
  uschar recvbuf[4096];
  uschar dcc_return_text[1024];
  uschar mbox_path[1024];
  uschar message_subdir[2];
  struct header_line *dcchdr;
  uschar *dcc_acl_options;
  uschar dcc_acl_options_buffer[10];
  uschar dcc_xtra_hdrs[1024];

  /* grep 1st option */
  if ((dcc_acl_options = string_nextinlist(&list, &sep,
                                           dcc_acl_options_buffer,
                                           sizeof(dcc_acl_options_buffer))) != NULL)
  {
    /* parse 1st option */
    if ( (strcmpic(dcc_acl_options,US"false") == 0) ||
         (Ustrcmp(dcc_acl_options,"0") == 0) ) {
      /* explicitly no matching */
      return FAIL;
    };

    /* special cases (match anything except empty) */
    if ( (strcmpic(dcc_acl_options,US"true") == 0) ||
         (Ustrcmp(dcc_acl_options,"*") == 0) ||
         (Ustrcmp(dcc_acl_options,"1") == 0) ) {
      dcc_acl_options = dcc_acl_options;
    };
  }
  else {
    /* empty means "don't match anything" */
    return FAIL;
  };

  sep = 0;

  /* if we scanned this message last time, just return */
  if ( dcc_ok )
      return dcc_rc;

  /* open the spooled body */
  message_subdir[1] = '\0';
  for (i = 0; i < 2; i++) {
    message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
    sprintf(CS mbox_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id);
    data_file = Ufopen(mbox_path,"rb");
    if (data_file != NULL)
      break;
  };

  if (data_file == NULL) {
    /* error while spooling */
    log_write(0, LOG_MAIN|LOG_PANIC,
           "dcc acl condition: error while opening spool file");
    return DEFER;
  };

  /* Initialize the variables */

  bzero(sockip,sizeof(sockip));
  if (dccifd_address) {
    if (dccifd_address[0] == '/')
      Ustrncpy(sockpath, dccifd_address, sizeof(sockpath));
    else
      if( sscanf(CS dccifd_address, "%s %u", sockip, &portnr) != 2) {
        log_write(0, LOG_MAIN,
          "dcc acl condition: warning - invalid dccifd address: '%s'", dccifd_address);
        (void)fclose(data_file);
        return DEFER;
      }
  }

  /* opts is what we send as dccifd options - see man dccifd */
  /* We don't support any other option than 'header' so just copy that */
  bzero(opts,sizeof(opts));
  Ustrncpy(opts, "header", sizeof(opts)-1);
  Ustrncpy(client_ip, dcc_ip_option, sizeof(client_ip)-1);
  /* If the dcc_client_ip is not provided use the
   * sender_host_address or 127.0.0.1 if it is NULL */
  DEBUG(D_acl)
    debug_printf("my_ip_option = %s - client_ip = %s - sender_host_address = %s\n", dcc_ip_option, client_ip, sender_host_address);
  if(!(Ustrcmp(client_ip, ""))){
    /* Do we have a sender_host_address or is it NULL? */
    if(sender_host_address){
      Ustrncpy(client_ip, sender_host_address, sizeof(client_ip)-1);
    } else {
      /* sender_host_address is NULL which means it comes from localhost */
      Ustrncpy(client_ip, dcc_default_ip_option, sizeof(client_ip)-1);
    }
  }
  DEBUG(D_acl)
    debug_printf("Client IP: %s\n", client_ip);
  Ustrncpy(sockip, dcc_daemon_ip, sizeof(sockip)-1);
  /* strncat(opts, my_request, strlen(my_request)); */
  Ustrcat(opts, "\n");
  Ustrncat(opts, client_ip, sizeof(opts)-Ustrlen(opts)-1);
  Ustrncat(opts, "\nHELO ", sizeof(opts)-Ustrlen(opts)-1);
  Ustrncat(opts, dcc_helo_option, sizeof(opts)-Ustrlen(opts)-2);
  Ustrcat(opts, "\n");

  /* initialize the other variables */
  dcchdr = header_list;
  /* we set the default return value to DEFER */
  retval = DEFER;

  bzero(sendbuf,sizeof(sendbuf));
  bzero(dcc_header_str,sizeof(dcc_header_str));
  bzero(rcpt,sizeof(rcpt));
  bzero(from,sizeof(from));

  /* send a null return path as "<>". */
  if (Ustrlen(sender_address) > 0)
    Ustrncpy(from, sender_address, sizeof(from));
  else
    Ustrncpy(from, "<>", sizeof(from));
  Ustrncat(from, "\n", sizeof(from)-Ustrlen(from)-1);

  /**************************************
   * Now creating the socket connection *
   **************************************/

  /* If there is a dcc_daemon_ip, we use a tcp socket, otherwise a UNIX socket */
  if(Ustrcmp(sockip, "")){
    ipaddress = gethostbyname((char *)sockip);
    bzero((char *) &serv_addr_in, sizeof(serv_addr_in));
    serv_addr_in.sin_family = AF_INET;
    bcopy((char *)ipaddress->h_addr, (char *)&serv_addr_in.sin_addr.s_addr, ipaddress->h_length);
    serv_addr_in.sin_port = htons(portnr);
    if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0){
      DEBUG(D_acl)
        debug_printf("Creating socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno));
      /* if we cannot create the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
    /* Now connecting the socket (INET) */
    if (connect(sockfd, (struct sockaddr *)&serv_addr_in, sizeof(serv_addr_in)) < 0){
      DEBUG(D_acl)
        debug_printf("Connecting socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno));
      /* if we cannot contact the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
  } else {
    /* connecting to the dccifd UNIX socket */
    bzero((char *)&serv_addr,sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    Ustrcpy(serv_addr.sun_path, sockpath);
    if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0){
      DEBUG(D_acl)
        debug_printf("Creating socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno));
      /* if we cannot create the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
    /* Now connecting the socket (UNIX) */
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
      DEBUG(D_acl)
                            debug_printf("Connecting socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno));
      /* if we cannot contact the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
  }
  /* the socket is open, now send the options to dccifd*/
  DEBUG(D_acl)
    debug_printf("\n---------------------------\nSocket opened; now sending input\n-----------------\n");
  /* First, fill in the input buffer */
  Ustrncpy(sendbuf, opts, sizeof(sendbuf));
  Ustrncat(sendbuf, from, sizeof(sendbuf)-Ustrlen(sendbuf)-1);

  DEBUG(D_acl)
  {
    debug_printf("opts = %s\nsender = %s\nrcpt count = %d\n", opts, from, recipients_count);
    debug_printf("Sending options:\n****************************\n");
  }

  /* let's send each of the recipients to dccifd */
  for (i = 0; i < recipients_count; i++){
    DEBUG(D_acl)
      debug_printf("recipient = %s\n",recipients_list[i].address);
    if(Ustrlen(sendbuf) + Ustrlen(recipients_list[i].address) > sizeof(sendbuf))
    {
      DEBUG(D_acl)
        debug_printf("Writing buffer: %s\n", sendbuf);
      flushbuffer(sockfd, sendbuf);
      bzero(sendbuf, sizeof(sendbuf));
    }
    Ustrncat(sendbuf, recipients_list[i].address, sizeof(sendbuf)-Ustrlen(sendbuf)-1);
    Ustrncat(sendbuf, "\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  }
  /* send a blank line between options and message */
  Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  /* Now we send the input buffer */
  DEBUG(D_acl)
    debug_printf("%s\n****************************\n", sendbuf);
  flushbuffer(sockfd, sendbuf);

  /* now send the message */
  /* Clear the input buffer */
  bzero(sendbuf, sizeof(sendbuf));
  /* First send the headers */
  /* Now send the headers */
  DEBUG(D_acl)
    debug_printf("Sending headers:\n****************************\n");
  Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2);
  while((dcchdr=dcchdr->next)) {
    if(dcchdr->slen > sizeof(sendbuf)-2) {
      /* The size of the header is bigger than the size of
       * the input buffer, so split it up in smaller parts. */
       flushbuffer(sockfd, sendbuf);
       bzero(sendbuf, sizeof(sendbuf));
       j = 0;
       while(j < dcchdr->slen)
       {
        for(i = 0; i < sizeof(sendbuf)-2; i++) {
          sendbuf[i] = dcchdr->text[j];
          j++;
        }
        flushbuffer(sockfd, sendbuf);
        bzero(sendbuf, sizeof(sendbuf));
       }
    } else if(Ustrlen(sendbuf) + dcchdr->slen > sizeof(sendbuf)-2) {
      flushbuffer(sockfd, sendbuf);
      bzero(sendbuf, sizeof(sendbuf));
      Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2);
    } else {
      Ustrncat(sendbuf, dcchdr->text, sizeof(sendbuf)-Ustrlen(sendbuf)-2);
    }
  }

  /* a blank line seperates header from body */
  Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  flushbuffer(sockfd, sendbuf);
  DEBUG(D_acl)
    debug_printf("\n****************************\n%s", sendbuf);

  /* Clear the input buffer */
  bzero(sendbuf, sizeof(sendbuf));

  /* now send the body */
  DEBUG(D_acl)
    debug_printf("Writing body:\n****************************\n");
  (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
  while((fread(sendbuf, 1, sizeof(sendbuf)-1, data_file)) > 0) {
    flushbuffer(sockfd, sendbuf);
    bzero(sendbuf, sizeof(sendbuf));
  }
  DEBUG(D_acl)
    debug_printf("\n****************************\n");

  /* shutdown() the socket */
  if(shutdown(sockfd, 1) < 0){
    DEBUG(D_acl)
      debug_printf("Couldn't shutdown socket: %s\n", strerror(errno));
    log_write(0,LOG_MAIN,"Couldn't shutdown socket: %s\n", strerror(errno));
    /* If there is a problem with the shutdown()
     * defer the mail. */
    (void)fclose(data_file);
    return retval;
  }
  DEBUG(D_acl)
    debug_printf("\n-------------------------\nInput sent.\n-------------------------\n");

    /********************************
   * receiving output from dccifd *
   ********************************/
  DEBUG(D_acl)
    debug_printf("\n-------------------------------------\nNow receiving output from server\n-----------------------------------\n");

  /******************************************************************
   * We should get 3 lines:                                         *
   * 1/ First line is overall result: either 'A' for Accept,        *
   *    'R' for Reject, 'S' for accept Some recipients or           *
   *    'T' for a Temporary error.                                  *
   * 2/ Second line contains the list of Accepted/Rejected          *
   *    recipients in the form AARRA (A = accepted, R = rejected).  *
   * 3/ Third line contains the X-DCC header.                       *
   ******************************************************************/

  line = 1;    /* we start at the first line of the output */
  j = 0;       /* will be used as index for the recipients list */
  k = 0;       /* initializing the index of the X-DCC header: dcc_header_str[k] */

  /* Let's read from the socket until there's nothing left to read */
  bzero(recvbuf, sizeof(recvbuf));
  while((resp = read(sockfd, recvbuf, sizeof(recvbuf)-1)) > 0) {
    /* How much did we get from the socket */
    c = Ustrlen(recvbuf) + 1;
    DEBUG(D_acl)
      debug_printf("Length of the output buffer is: %d\nOutput buffer is:\n------------\n%s\n-----------\n", c, recvbuf);

    /* Now let's read each character and see what we've got */
    for(i = 0; i < c; i++) {
      /* First check if we reached the end of the line and
       * then increment the line counter */
      if(recvbuf[i] == '\n') {
        line++;
      }
      else {
        /* The first character of the first line is the
         * overall response. If there's another character
         * on that line it is not correct. */
        if(line == 1) {
          if(i == 0) {
            /* Now get the value and set the
             * return value accordingly */
            if(recvbuf[i] == 'A') {
              DEBUG(D_acl)
                debug_printf("Overall result = A\treturning OK\n");
              Ustrcpy(dcc_return_text, "Mail accepted by DCC");
              dcc_result = US"A";
              retval = OK;
            }
            else if(recvbuf[i] == 'R') {
              DEBUG(D_acl)
                debug_printf("Overall result = R\treturning FAIL\n");
              dcc_result = US"R";
              retval = FAIL;
              if(sender_host_name) {
                log_write(0, LOG_MAIN, "H=%s [%s] F=<%s>: rejected by DCC", sender_host_name, sender_host_address, sender_address);
              }
              else {
                log_write(0, LOG_MAIN, "H=[%s] F=<%s>: rejected by DCC", sender_host_address, sender_address);
              }
              Ustrncpy(dcc_return_text, dcc_reject_message, Ustrlen(dcc_reject_message) + 1);
            }
            else if(recvbuf[i] == 'S') {
              DEBUG(D_acl)
                debug_printf("Overall result  = S\treturning OK\n");
              Ustrcpy(dcc_return_text, "Not all recipients accepted by DCC");
              /* Since we're in an ACL we want a global result
               * so we accept for all */
              dcc_result = US"A";
              retval = OK;
            }
            else if(recvbuf[i] == 'G') {
              DEBUG(D_acl)
                debug_printf("Overall result  = G\treturning FAIL\n");
              Ustrcpy(dcc_return_text, "Greylisted by DCC");
              dcc_result = US"G";
              retval = FAIL;
            }
            else if(recvbuf[i] == 'T') {
              DEBUG(D_acl)
                debug_printf("Overall result = T\treturning DEFER\n");
              retval = DEFER;
              log_write(0,LOG_MAIN,"Temporary error with DCC: %s\n", recvbuf);
              Ustrcpy(dcc_return_text, "Temporary error with DCC");
              dcc_result = US"T";
            }
            else {
              DEBUG(D_acl)
                debug_printf("Overall result = something else\treturning DEFER\n");
              retval = DEFER;
              log_write(0,LOG_MAIN,"Unknown DCC response: %s\n", recvbuf);
              Ustrcpy(dcc_return_text, "Unknown DCC response");
              dcc_result = US"T";
            }
          }
          else {
          /* We're on the first line but not on the first character,
           * there must be something wrong. */
            DEBUG(D_acl)
              debug_printf("Line = %d but i = %d != 0  character is %c - This is wrong!\n", line, i, recvbuf[i]);
              log_write(0,LOG_MAIN,"Wrong header from DCC, output is %s\n", recvbuf);
          }
        }
        else if(line == 2) {
          /* On the second line we get a list of
           * answers for each recipient. We don't care about
           * it because we're in an acl and take the
           * global result. */
        }
        else if(line > 2) {
          /* The third and following lines are the X-DCC header,
           * so we store it in dcc_header_str. */
          /* check if we don't get more than we can handle */
          if(k < sizeof(dcc_header_str)) { 
            dcc_header_str[k] = recvbuf[i];
            k++;
          }
          else {
            DEBUG(D_acl)
              debug_printf("We got more output than we can store in the X-DCC header. Truncating at 120 characters.\n");
          }
        }
        else {
          /* Wrong line number. There must be a problem with the output. */
          DEBUG(D_acl)
            debug_printf("Wrong line number in output. Line number is %d\n", line);
        }
      }
    }
    /* we reinitialize the output buffer before we read again */
    bzero(recvbuf,sizeof(recvbuf));
  }
  /* We have read everything from the socket */

  /* We need to terminate the X-DCC header with a '\n' character. This needs to be k-1
   * since dcc_header_str[k] contains '\0'. */
  dcc_header_str[k-1] = '\n';

  /* Now let's sum up what we've got. */
  DEBUG(D_acl)
    debug_printf("\n--------------------------\nOverall result = %d\nX-DCC header: %sReturn message: %s\ndcc_result: %s\n", retval, dcc_header_str, dcc_return_text, dcc_result);

  /* We only add the X-DCC header if it starts with X-DCC */
  if(!(Ustrncmp(dcc_header_str, "X-DCC", 5))){
    dcc_header = dcc_header_str;
    if(dcc_direct_add_header) {
      header_add(' ' , "%s", dcc_header_str);
  /* since the MIME ACL already writes the .eml file to disk without DCC Header we've to erase it */
      unspool_mbox();
    }
  }
  else {
    DEBUG(D_acl)
      debug_printf("Wrong format of the X-DCC header: %s\n", dcc_header_str);
  }

  /* check if we should add additional headers passed in acl_m_dcc_add_header */
  if(dcc_direct_add_header) {
    if (((xtra_hdrs = expand_string(US"$acl_m_dcc_add_header")) != NULL) && (xtra_hdrs[0] != '\0')) {
      Ustrncpy(dcc_xtra_hdrs, xtra_hdrs, sizeof(dcc_xtra_hdrs) - 2);
      if (dcc_xtra_hdrs[Ustrlen(dcc_xtra_hdrs)-1] != '\n')
        Ustrcat(dcc_xtra_hdrs, "\n");
      header_add(' ', "%s", dcc_xtra_hdrs);
      DEBUG(D_acl)
        debug_printf("adding additional headers in $acl_m_dcc_add_header: %s", dcc_xtra_hdrs);
    }
  }

  dcc_ok = 1;
  /* Now return to exim main process */
  DEBUG(D_acl)
    debug_printf("Before returning to exim main process:\nreturn_text = %s - retval = %d\ndcc_result = %s\n", dcc_return_text, retval, dcc_result);

  (void)fclose(data_file);
  dcc_rc = retval;
  return dcc_rc;
}
Esempio n. 5
0
 void load_usersrs(_lusers_s * l_users) {

         _lusers_s                       * tmp           = l_users;
         char                            * message       = malloc(1);
         DSPAM_CTX                       * CTX           = NULL; /** DSPAM Context */
         struct _ds_spam_signature       SIG;                    /** Example signature */

         if (tmp == NULL) {
                 memset(message, 0x0, sizeof(message));
                 free(message);
                 return;
         }

         message = read_emailmem(message);

         while (tmp != NULL) {

                 CTX = dspam_create((char *)tmp->realemail, NULL, NULL, DSM_PROCESS, DSF_CHAINED | DSF_SIGNATURE | DSF_NOISE);
                 CTX = attach_ctx_dbaccess(CTX);
                 if (CTX == NULL) {
                         log_write(0, LOG_MAIN, "dspam_create failed!");
                         break;
                 }
                 if (dspam_process(CTX, message) != 0) { /** Call DSPAM's processor with the message text */
                         log_write(0, LOG_MAIN, "dspam_process failed!");
                         dspam_destroy(CTX);
                         break;
                 }
                 if (CTX->result == DSR_ISSPAM) {     /** Print processing results */
                         log_write(0, LOG_MAIN, "spam->[%s]:\n\tProbability:\t[%2.4f]\n\tConfidence:\t[%2.4f]",
                                 (char *)tmp->realemail, CTX->probability, CTX->confidence);
                         header_add(' ', "%s: %s\n", (char *)spamflag, (char *)tmp->realemail);
                         lscan.spamflag = SPAMFLAG;
                 } else {
                         log_write(0, LOG_MAIN, "not spam->[%s]", (char *)tmp->realemail);
                         lscan.spamflag = 0;
                 }

                 if (CTX->signature != NULL) {
                         SIG.data = malloc(CTX->signature->length);
                         if (SIG.data != NULL) {
                                 memcpy(SIG.data, CTX->signature->data, CTX->signature->length);
                         }
                 }
                 SIG.length = CTX->signature->length;

                 if (dspam_destroy(CTX) != 0) {          /** Destroy the context */
                         log_write(0, LOG_MAIN, "dspam_destroy failed!");
                         break;
                 }

                 tmp = (_lusers_s *)tmp->next;                /** Move on to next user */
         }

         memset(message, 0x0, sizeof(message));
         free(message);
         memset(&CTX, 0x0, sizeof(CTX));
         free(CTX);

         return;
 } /** load_usersrs */
Esempio n. 6
0
File: rda.c Progetto: toddr/exim
int
rda_interpret(redirect_block *rdata, int options, uschar *include_directory,
  uschar *sieve_vacation_directory, uschar *sieve_enotify_mailto_owner,
  uschar *sieve_useraddress, uschar *sieve_subaddress, ugid_block *ugid,
  address_item **generated, uschar **error, error_block **eblockp,
  int *filtertype, uschar *rname)
{
int fd, rc, pfd[2];
int yield, status;
BOOL had_disaster = FALSE;
pid_t pid;
uschar *data;
uschar *readerror = US"";
void (*oldsignal)(int);

DEBUG(D_route) debug_printf("rda_interpret (%s): %s\n",
  (rdata->isfile)? "file" : "string", rdata->string);

/* Do the expansions of the file name or data first, while still privileged. */

data = expand_string(rdata->string);
if (data == NULL)
  {
  if (expand_string_forcedfail) return FF_NOTDELIVERED;
  *error = string_sprintf("failed to expand \"%s\": %s", rdata->string,
    expand_string_message);
  return FF_ERROR;
  }
rdata->string = data;

DEBUG(D_route) debug_printf("expanded: %s\n", data);

if (rdata->isfile && data[0] != '/')
  {
  *error = string_sprintf("\"%s\" is not an absolute path", data);
  return FF_ERROR;
  }

/* If no uid/gid are supplied, or if we have a data string which does not start
with #Exim filter or #Sieve filter, and does not contain :include:, do all the
work in this process. Note that for a system filter, we always have a file, so
the work is done in this process only if no user is supplied. */

if (!ugid->uid_set ||                         /* Either there's no uid, or */
    (!rdata->isfile &&                        /* We've got the data, and */
     rda_is_filter(data) == FILTER_FORWARD && /* It's not a filter script, */
     Ustrstr(data, ":include:") == NULL))     /* and there's no :include: */
  {
  return rda_extract(rdata, options, include_directory,
    sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
    sieve_subaddress, generated, error, eblockp, filtertype);
  }

/* We need to run the processing code in a sub-process. However, if we can
determine the non-existence of a file first, we can decline without having to
create the sub-process. */

if (rdata->isfile && rda_exists(data, error) == FILE_NOT_EXIST)
  return FF_NONEXIST;

/* If the file does exist, or we can't tell (non-root mounted NFS directory)
we have to create the subprocess to do everything as the given user. The
results of processing are passed back via a pipe. */

if (pipe(pfd) != 0)
  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "creation of pipe for filter or "
    ":include: failed for %s: %s", rname, strerror(errno));

/* Ensure that SIGCHLD is set to SIG_DFL before forking, so that the child
process can be waited for. We sometimes get here with it set otherwise. Save
the old state for resetting on the wait. Ensure that all cached resources are
freed so that the subprocess starts with a clean slate and doesn't interfere
with the parent process. */

oldsignal = signal(SIGCHLD, SIG_DFL);
search_tidyup();

if ((pid = fork()) == 0)
  {
  header_line *waslast = header_last;   /* Save last header */

  fd = pfd[pipe_write];
  (void)close(pfd[pipe_read]);
  exim_setugid(ugid->uid, ugid->gid, FALSE, rname);

  /* Addresses can get rewritten in filters; if we are not root or the exim
  user (and we probably are not), turn off rewrite logging, because we cannot
  write to the log now. */

  if (ugid->uid != root_uid && ugid->uid != exim_uid)
    {
    DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
      "root or exim in this process)\n");
    BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite);
    }

  /* Now do the business */

  yield = rda_extract(rdata, options, include_directory,
    sieve_vacation_directory, sieve_enotify_mailto_owner, sieve_useraddress,
    sieve_subaddress, generated, error, eblockp, filtertype);

  /* Pass back whether it was a filter, and the return code and any overall
  error text via the pipe. */

  if (  write(fd, filtertype, sizeof(int)) != sizeof(int)
     || write(fd, &yield, sizeof(int)) != sizeof(int)
     || rda_write_string(fd, *error) != 0
     )
    goto bad;

  /* Pass back the contents of any syntax error blocks if we have a pointer */

  if (eblockp != NULL)
    {
    error_block *ep;
    for (ep = *eblockp; ep != NULL; ep = ep->next)
      if (  rda_write_string(fd, ep->text1) != 0
         || rda_write_string(fd, ep->text2) != 0
	 )
	goto bad;
    if (rda_write_string(fd, NULL) != 0)    /* Indicates end of eblocks */
      goto bad;
    }

  /* If this is a system filter, we have to pass back the numbers of any
  original header lines that were removed, and then any header lines that were
  added but not subsequently removed. */

  if (system_filtering)
    {
    int i = 0;
    header_line *h;
    for (h = header_list; h != waslast->next; i++, h = h->next)
      if (  h->type == htype_old
         && write(fd, &i, sizeof(i)) != sizeof(i)
	 )
	goto bad;

    i = -1;
    if (write(fd, &i, sizeof(i)) != sizeof(i))
	goto bad;

    while (waslast != header_last)
      {
      waslast = waslast->next;
      if (waslast->type != htype_old)
	if (  rda_write_string(fd, waslast->text) != 0
           || write(fd, &(waslast->type), sizeof(waslast->type))
	      != sizeof(waslast->type)
	   )
	  goto bad;
      }
    if (rda_write_string(fd, NULL) != 0)    /* Indicates end of added headers */
      goto bad;
    }

  /* Write the contents of the $n variables */

  if (write(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n))
    goto bad;

  /* If the result was DELIVERED or NOTDELIVERED, we pass back the generated
  addresses, and their associated information, through the pipe. This is
  just tedious, but it seems to be the only safe way. We do this also for
  FAIL and FREEZE, because a filter is allowed to set up deliveries that
  are honoured before freezing or failing. */

  if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
      yield == FF_FAIL || yield == FF_FREEZE)
    {
    address_item *addr;
    for (addr = *generated; addr != NULL; addr = addr->next)
      {
      int reply_options = 0;

      if (  rda_write_string(fd, addr->address) != 0
         || write(fd, &(addr->mode), sizeof(addr->mode))
	    != sizeof(addr->mode)
         || write(fd, &(addr->flags), sizeof(addr->flags))
	    != sizeof(addr->flags)
         || rda_write_string(fd, addr->prop.errors_address) != 0
	 )
	goto bad;

      if (addr->pipe_expandn != NULL)
        {
        uschar **pp;
        for (pp = addr->pipe_expandn; *pp != NULL; pp++)
          if (rda_write_string(fd, *pp) != 0)
	    goto bad;
        }
      if (rda_write_string(fd, NULL) != 0)
        goto bad;

      if (addr->reply == NULL)
	{
        if (write(fd, &reply_options, sizeof(int)) != sizeof(int))    /* 0 means no reply */
	  goto bad;
	}
      else
        {
        reply_options |= REPLY_EXISTS;
        if (addr->reply->file_expand) reply_options |= REPLY_EXPAND;
        if (addr->reply->return_message) reply_options |= REPLY_RETURN;
        if (  write(fd, &reply_options, sizeof(int)) != sizeof(int)
           || write(fd, &(addr->reply->expand_forbid), sizeof(int))
	      != sizeof(int)
           || write(fd, &(addr->reply->once_repeat), sizeof(time_t))
	      != sizeof(time_t)
           || rda_write_string(fd, addr->reply->to) != 0
           || rda_write_string(fd, addr->reply->cc) != 0
           || rda_write_string(fd, addr->reply->bcc) != 0
           || rda_write_string(fd, addr->reply->from) != 0
           || rda_write_string(fd, addr->reply->reply_to) != 0
           || rda_write_string(fd, addr->reply->subject) != 0
           || rda_write_string(fd, addr->reply->headers) != 0
           || rda_write_string(fd, addr->reply->text) != 0
           || rda_write_string(fd, addr->reply->file) != 0
           || rda_write_string(fd, addr->reply->logfile) != 0
           || rda_write_string(fd, addr->reply->oncelog) != 0
	   )
	  goto bad;
        }
      }

    if (rda_write_string(fd, NULL) != 0)   /* Marks end of addresses */
      goto bad;
    }

  /* OK, this process is now done. Free any cached resources. Must use _exit()
  and not exit() !! */

out:
  (void)close(fd);
  search_tidyup();
  _exit(0);

bad:
  DEBUG(D_rewrite) debug_printf("rda_interpret: failed write to pipe\n");
  goto out;
  }

/* Back in the main process: panic if the fork did not succeed. */

if (pid < 0)
  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for %s", rname);

/* Read the pipe to get the data from the filter/forward. Our copy of the
writing end must be closed first, as otherwise read() won't return zero on an
empty pipe. Afterwards, close the reading end. */

(void)close(pfd[pipe_write]);

/* Read initial data, including yield and contents of *error */

fd = pfd[pipe_read];
if (read(fd, filtertype, sizeof(int)) != sizeof(int) ||
    read(fd, &yield, sizeof(int)) != sizeof(int) ||
    !rda_read_string(fd, error)) goto DISASTER;

/* Read the contents of any syntax error blocks if we have a pointer */

if (eblockp != NULL)
  {
  uschar *s;
  error_block *e;
  error_block **p = eblockp;
  for (;;)
    {
    if (!rda_read_string(fd, &s)) goto DISASTER;
    if (s == NULL) break;
    e = store_get(sizeof(error_block));
    e->next = NULL;
    e->text1 = s;
    if (!rda_read_string(fd, &s)) goto DISASTER;
    e->text2 = s;
    *p = e;
    p = &(e->next);
    }
  }

/* If this is a system filter, read the identify of any original header lines
that were removed, and then read data for any new ones that were added. */

if (system_filtering)
  {
  int hn = 0;
  header_line *h = header_list;

  for (;;)
    {
    int n;
    if (read(fd, &n, sizeof(int)) != sizeof(int)) goto DISASTER;
    if (n < 0) break;
    while (hn < n)
      {
      hn++;
      h = h->next;
      if (h == NULL) goto DISASTER_NO_HEADER;
      }
    h->type = htype_old;
    }

  for (;;)
    {
    uschar *s;
    int type;
    if (!rda_read_string(fd, &s)) goto DISASTER;
    if (s == NULL) break;
    if (read(fd, &type, sizeof(type)) != sizeof(type)) goto DISASTER;
    header_add(type, "%s", s);
    }
  }

/* Read the values of the $n variables */

if (read(fd, filter_n, sizeof(filter_n)) != sizeof(filter_n)) goto DISASTER;

/* If the yield is DELIVERED, NOTDELIVERED, FAIL, or FREEZE there may follow
addresses and data to go with them. Keep them in the same order in the
generated chain. */

if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED ||
    yield == FF_FAIL || yield == FF_FREEZE)
  {
  address_item **nextp = generated;

  for (;;)
    {
    int i, reply_options;
    address_item *addr;
    uschar *recipient;
    uschar *expandn[EXPAND_MAXN + 2];

    /* First string is the address; NULL => end of addresses */

    if (!rda_read_string(fd, &recipient)) goto DISASTER;
    if (recipient == NULL) break;

    /* Hang on the end of the chain */

    addr = deliver_make_addr(recipient, FALSE);
    *nextp = addr;
    nextp = &(addr->next);

    /* Next comes the mode and the flags fields */

    if (read(fd, &(addr->mode), sizeof(addr->mode)) != sizeof(addr->mode) ||
        read(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) ||
        !rda_read_string(fd, &(addr->prop.errors_address))) goto DISASTER;

    /* Next comes a possible setting for $thisaddress and any numerical
    variables for pipe expansion, terminated by a NULL string. The maximum
    number of numericals is EXPAND_MAXN. Note that we put filter_thisaddress
    into the zeroth item in the vector - this is sorted out inside the pipe
    transport. */

    for (i = 0; i < EXPAND_MAXN + 1; i++)
      {
      uschar *temp;
      if (!rda_read_string(fd, &temp)) goto DISASTER;
      if (i == 0) filter_thisaddress = temp;           /* Just in case */
      expandn[i] = temp;
      if (temp == NULL) break;
      }

    if (i > 0)
      {
      addr->pipe_expandn = store_get((i+1) * sizeof(uschar **));
      addr->pipe_expandn[i] = NULL;
      while (--i >= 0) addr->pipe_expandn[i] = expandn[i];
      }

    /* Then an int containing reply options; zero => no reply data. */

    if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER;
    if ((reply_options & REPLY_EXISTS) != 0)
      {
      addr->reply = store_get(sizeof(reply_item));

      addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0;
      addr->reply->return_message = (reply_options & REPLY_RETURN) != 0;

      if (read(fd,&(addr->reply->expand_forbid),sizeof(int)) !=
            sizeof(int) ||
          read(fd,&(addr->reply->once_repeat),sizeof(time_t)) !=
            sizeof(time_t) ||
          !rda_read_string(fd, &(addr->reply->to)) ||
          !rda_read_string(fd, &(addr->reply->cc)) ||
          !rda_read_string(fd, &(addr->reply->bcc)) ||
          !rda_read_string(fd, &(addr->reply->from)) ||
          !rda_read_string(fd, &(addr->reply->reply_to)) ||
          !rda_read_string(fd, &(addr->reply->subject)) ||
          !rda_read_string(fd, &(addr->reply->headers)) ||
          !rda_read_string(fd, &(addr->reply->text)) ||
          !rda_read_string(fd, &(addr->reply->file)) ||
          !rda_read_string(fd, &(addr->reply->logfile)) ||
          !rda_read_string(fd, &(addr->reply->oncelog)))
        goto DISASTER;
      }
    }
  }

/* All data has been transferred from the sub-process. Reap it, close the
reading end of the pipe, and we are done. */

WAIT_EXIT:
while ((rc = wait(&status)) != pid)
  {
  if (rc < 0 && errno == ECHILD)      /* Process has vanished */
    {
    log_write(0, LOG_MAIN, "redirection process %d vanished unexpectedly", pid);
    goto FINAL_EXIT;
    }
  }

DEBUG(D_route)
  debug_printf("rda_interpret: subprocess yield=%d error=%s\n", yield, *error);

if (had_disaster)
  {
  *error = string_sprintf("internal problem in %s: failure to transfer "
    "data from subprocess: status=%04x%s%s%s", rname,
    status, readerror,
    (*error == NULL)? US"" : US": error=",
    (*error == NULL)? US"" : *error);
  log_write(0, LOG_MAIN|LOG_PANIC, "%s", *error);
  }
else if (status != 0)
  {
  log_write(0, LOG_MAIN|LOG_PANIC, "internal problem in %s: unexpected status "
    "%04x from redirect subprocess (but data correctly received)", rname,
    status);
  }

FINAL_EXIT:
(void)close(fd);
signal(SIGCHLD, oldsignal);   /* restore */
return yield;


/* Come here if the data indicates removal of a header that we can't find */

DISASTER_NO_HEADER:
readerror = US" readerror=bad header identifier";
had_disaster = TRUE;
yield = FF_ERROR;
goto WAIT_EXIT;

/* Come here is there's a shambles in transferring the data over the pipe. The
value of errno should still be set. */

DISASTER:
readerror = string_sprintf(" readerror='%s'", strerror(errno));
had_disaster = TRUE;
yield = FF_ERROR;
goto WAIT_EXIT;
}
Esempio n. 7
0
int xport_to_queue(FILE *log)
{
    struct xport xpt;
    struct queue que;

    unsigned char* buf = 0;
    size_t bufsiz;

    enqueuer_stats_ctor(&est);

    if ( arg_rt )
    {
        skd(log);
    }

    if ( (queue_factory(&que, log) < 0) || (que.vtbl->open(&que, O_WRONLY) < 0) )
    {
        LOG_ER(log, "Failed to create or open queue object.\n");
        exit(EXIT_FAILURE);
    }

    /* Can we drop root here? */

    if ( (xport_factory(&xpt, log) < 0) || (xpt.vtbl->open(&xpt, O_RDONLY) < 0) )
    {
        LOG_ER(log, "Failed to create xport object.\n");
        exit(EXIT_FAILURE);
    }

    buf = (unsigned char*)que.vtbl->alloc(&que, &bufsiz);
    if ( 0 == buf )
    {
        LOG_ER(log, "unable to allocate %d bytes for message buffer.\n", bufsiz);
        exit(EXIT_FAILURE);
    }
    memset(buf, 0, bufsiz);

    /* Read a packet from the transport, write it to the queue. */
    while ( ! gbl_done )
    {
        int que_write_ret;

        unsigned long long tm;
        unsigned long addr;
        short port;

        /* 0 out part of the the event name so if we get a rotate event
         * the program will not continually rotate */
        memset(buf, 0, HEADER_LENGTH+20);

        int xpt_read_ret = xpt.vtbl->read(&xpt,
                                          buf + HEADER_LENGTH,
                                          bufsiz - HEADER_LENGTH,
                                          &addr, &port);
        if (xpt_read_ret >= 0 )
        {
            tm = millis_now ();
            enqueuer_stats_record_datagram(&est, xpt_read_ret + HEADER_LENGTH);
            header_add(buf, xpt_read_ret, tm, addr, port);
        }
        else if (xpt_read_ret == XPORT_INTR)
        {
            /* ignore expected interrupts */;
        }
        else
        {
            LOG_INF(log, "Received other interruption\n");
            enqueuer_stats_record_socket_error(&est);
            continue;
        }

        /* we are rotating or shutting down */
        if ( header_is_rotate (buf) || gbl_rotate_enqueue || gbl_done)
        {
            /* if we are shutting down the destructor will flush stats,
             * so skip so we don't get duplicate events sent to mondemand
             * if it is enabled
             */
            if (! gbl_done )
            {
                enqueuer_stats_rotate(&est, log);
                enqueuer_stats_flush (&est);
            }
            if (gbl_rotate_enqueue)
            {
                CAS_OFF(gbl_rotate_enqueue);
            }
        }

        if (gbl_rotate_enqueue_log)
        {
            log = get_log (log);
            CAS_OFF(gbl_rotate_enqueue_log);
        }

        if (xpt_read_ret != XPORT_INTR)
        {
            if ( (que_write_ret = que.vtbl->write(&que,
                                                  buf,
                                                  xpt_read_ret + HEADER_LENGTH)) < 0 )
            {
                LOG_ER(log, "Queue write error attempting to write %d bytes.\n",
                       xpt_read_ret + HEADER_LENGTH);
                continue;
            }
            else
            {
                LOG_PROG(log, "Queue write of %d bytes.\n",
                         xpt_read_ret + HEADER_LENGTH);
            }
        }
    }

    que.vtbl->dealloc(&que, buf);

    xpt.vtbl->destructor(&xpt);
    que.vtbl->destructor(&que);

    enqueuer_stats_rotate(&est, log);
    enqueuer_stats_report(&est, log);
    enqueuer_stats_dtor(&est);

    return 0;
}