Example #1
0
File: header.c Project: hillu/exim
BOOL
header_testname_incomplete(header_line *h, uschar *name, int len, BOOL notdel)
{
if (h->type == '*' && notdel) return FALSE;
if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE;
return TRUE;
}
Example #2
0
File: header.c Project: hillu/exim
BOOL
header_testname(header_line *h, uschar *name, int len, BOOL notdel)
{
uschar *tt;
if (h->type == '*' && notdel) return FALSE;
if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE;
tt = h->text + len;
while (*tt == ' ' || *tt == '\t') tt++;
return *tt == ':';
}
Example #3
0
/*********************************************************************
 *
 * Function    :  create_pattern_spec
 *
 * Description :  Creates a "pattern_spec" structure from a string.
 *                When finished, free with free_pattern_spec().
 *
 * Parameters  :
 *          1  :  pattern = Target pattern_spec to be filled in.
 *                          Will be zeroed before use.
 *          2  :  buf = Source pattern, null terminated.  NOTE: The
 *                      contents of this buffer are destroyed by this
 *                      function.  If this function succeeds, the
 *                      buffer is copied to pattern->spec.  If this
 *                      function fails, the contents of the buffer
 *                      are lost forever.
 *
 * Returns     :  JB_ERR_OK - Success
 *                JB_ERR_PARSE - Cannot parse regex (Detailed message
 *                               written to system log)
 *
 *********************************************************************/
jb_err create_pattern_spec(struct pattern_spec *pattern, char *buf)
{
   static const struct
   {
      /** The tag pattern prefix to match */
      const char *prefix;

      /** The length of the prefix to match */
      const size_t prefix_length;

      /** The pattern flag */
      const unsigned flag;
   } tag_pattern[] = {
      { "TAG:",              4, PATTERN_SPEC_TAG_PATTERN},
      { "NO-REQUEST-TAG:",  15, PATTERN_SPEC_NO_REQUEST_TAG_PATTERN},
      { "NO-RESPONSE-TAG:", 16, PATTERN_SPEC_NO_RESPONSE_TAG_PATTERN}
   };
   int i;

   assert(pattern);
   assert(buf);

   memset(pattern, '\0', sizeof(*pattern));

   /* Remember the original specification for the CGI pages. */
   pattern->spec = strdup_or_die(buf);

   /* Check if it's a tag pattern */
   for (i = 0; i < SZ(tag_pattern); i++)
   {
      if (0 == strncmpic(pattern->spec, tag_pattern[i].prefix, tag_pattern[i].prefix_length))
      {
         /* The regex starts after the prefix */
         const char *tag_regex = buf + tag_pattern[i].prefix_length;

         pattern->flags |= tag_pattern[i].flag;

         return compile_pattern(tag_regex, NO_ANCHORING, pattern,
            &pattern->pattern.tag_regex);
      }
   }

   /* If it isn't a tag pattern it must be an URL pattern. */
   pattern->flags |= PATTERN_SPEC_URL_PATTERN;

   return compile_url_pattern(pattern, buf);

}
struct parsers *
match(char *buf, struct parsers *pats)
{
	struct parsers *v;

	if(buf == NULL) {
		/* hit me */
		fprintf(logfp, "bingo!\n");
		return(NULL);
	}

	for(v = pats; v->str ; v++) {
		if(strncmpic(buf, v->str, v->len) == 0) {
			return(v);
		}
	}
	return(NULL);
}
Example #5
0
BOOL
autoreply_transport_entry(
  transport_instance *tblock,      /* data for this instantiation */
  address_item *addr)              /* address we are working on */
{
int fd, pid, rc;
int cache_fd = -1;
int log_fd = -1;
int cache_size = 0;
int add_size = 0;
EXIM_DB *dbm_file = NULL;
BOOL file_expand, return_message;
uschar *from, *reply_to, *to, *cc, *bcc, *subject, *headers, *text, *file;
uschar *logfile, *oncelog;
uschar *cache_buff = NULL;
uschar *cache_time = NULL;
uschar *message_id = NULL;
header_line *h;
time_t now = time(NULL);
time_t once_repeat_sec = 0;
FILE *f;
FILE *ff = NULL;

autoreply_transport_options_block *ob =
  (autoreply_transport_options_block *)(tblock->options_block);

DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name);

/* Set up for the good case */

addr->transport_return = OK;
addr->basic_errno = 0;

/* If the address is pointing to a reply block, then take all the data
from that block. It has typically been set up by a mail filter processing
router. Otherwise, the data must be supplied by this transport, and
it has to be expanded here. */

if (addr->reply != NULL)
  {
  DEBUG(D_transport) debug_printf("taking data from address\n");
  from = addr->reply->from;
  reply_to = addr->reply->reply_to;
  to = addr->reply->to;
  cc = addr->reply->cc;
  bcc = addr->reply->bcc;
  subject = addr->reply->subject;
  headers = addr->reply->headers;
  text = addr->reply->text;
  file = addr->reply->file;
  logfile = addr->reply->logfile;
  oncelog = addr->reply->oncelog;
  once_repeat_sec = addr->reply->once_repeat;
  file_expand = addr->reply->file_expand;
  expand_forbid = addr->reply->expand_forbid;
  return_message = addr->reply->return_message;
  }
else
  {
  uschar *oncerepeat = ob->once_repeat;

  DEBUG(D_transport) debug_printf("taking data from transport\n");
  from = ob->from;
  reply_to = ob->reply_to;
  to = ob->to;
  cc = ob->cc;
  bcc = ob->bcc;
  subject = ob->subject;
  headers = ob->headers;
  text = ob->text;
  file = ob->file;
  logfile = ob->logfile;
  oncelog = ob->oncelog;
  file_expand = ob->file_expand;
  return_message = ob->return_message;

  if ((from  != NULL &&
        (from = checkexpand(from, addr, tblock->name, cke_hdr)) == NULL) ||
      (reply_to    != NULL &&
        (reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr)) == NULL) ||
      (to    != NULL &&
        (to = checkexpand(to, addr, tblock->name, cke_hdr)) == NULL) ||
      (cc    != NULL &&
        (cc = checkexpand(cc, addr, tblock->name, cke_hdr)) == NULL) ||
      (bcc   != NULL &&
        (bcc = checkexpand(bcc, addr, tblock->name, cke_hdr)) == NULL) ||
      (subject   != NULL &&
        (subject = checkexpand(subject, addr, tblock->name, cke_hdr)) == NULL) ||
      (headers != NULL &&
        (headers = checkexpand(headers, addr, tblock->name, cke_text)) == NULL) ||
      (text  != NULL &&
        (text = checkexpand(text, addr, tblock->name, cke_text)) == NULL) ||
      (file  != NULL &&
        (file = checkexpand(file, addr, tblock->name, cke_file)) == NULL) ||
      (logfile != NULL &&
        (logfile = checkexpand(logfile, addr, tblock->name, cke_file)) == NULL) ||
      (oncelog != NULL &&
        (oncelog = checkexpand(oncelog, addr, tblock->name, cke_file)) == NULL) ||
      (oncerepeat != NULL &&
        (oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file)) == NULL))
    return FALSE;

  if (oncerepeat != NULL)
    {
    once_repeat_sec = readconf_readtime(oncerepeat, 0, FALSE);
    if (once_repeat_sec < 0)
      {
      addr->transport_return = FAIL;
      addr->message = string_sprintf("Invalid time value \"%s\" for "
        "\"once_repeat\" in %s transport", oncerepeat, tblock->name);
      return FALSE;
      }
    }
  }

/* If the never_mail option is set, we have to scan all the recipients and
remove those that match. */

if (ob->never_mail != NULL)
  {
  uschar *never_mail = expand_string(ob->never_mail);

  if (never_mail == NULL)
    {
    addr->transport_return = FAIL;
    addr->message = string_sprintf("Failed to expand \"%s\" for "
      "\"never_mail\" in %s transport", ob->never_mail, tblock->name);
    return FALSE;
    }

  if (to != NULL) check_never_mail(&to, never_mail);
  if (cc != NULL) check_never_mail(&cc, never_mail);
  if (bcc != NULL) check_never_mail(&bcc, never_mail);

  if (to == NULL && cc == NULL && bcc == NULL)
    {
    DEBUG(D_transport)
      debug_printf("*** all recipients removed by never_mail\n");
    return OK;
    }
  }

/* If the -N option is set, can't do any more. */

if (dont_deliver)
  {
  DEBUG(D_transport)
    debug_printf("*** delivery by %s transport bypassed by -N option\n",
      tblock->name);
  return FALSE;
  }


/* If the oncelog field is set, we send want to send only one message to the
given recipient(s). This works only on the "To" field. If there is no "To"
field, the message is always sent. If the To: field contains more than one
recipient, the effect might not be quite as envisaged. If once_file_size is
set, instead of a dbm file, we use a regular file containing a circular buffer
recipient cache. */

if (oncelog != NULL && *oncelog != 0 && to != NULL)
  {
  time_t then = 0;

  /* Handle fixed-size cache file. */

  if (ob->once_file_size > 0)
    {
    uschar *p;
    struct stat statbuf;
    cache_fd = Uopen(oncelog, O_CREAT|O_RDWR, ob->mode);

    if (cache_fd < 0 || fstat(cache_fd, &statbuf) != 0)
      {
      addr->transport_return = DEFER;
      addr->message = string_sprintf("Failed to %s \"once\" file %s when "
        "sending message from %s transport: %s",
        (cache_fd < 0)? "open" : "stat", oncelog, tblock->name,
          strerror(errno));
      goto END_OFF;
      }

    /* Get store in the temporary pool and read the entire file into it. We get
    an amount of store that is big enough to add the new entry on the end if we
    need to do that. */

    cache_size = statbuf.st_size;
    add_size = sizeof(time_t) + Ustrlen(to) + 1;
    cache_buff = store_get(cache_size + add_size);

    if (read(cache_fd, cache_buff, cache_size) != cache_size)
      {
      addr->transport_return = DEFER;
      addr->basic_errno = errno;
      addr->message = US"error while reading \"once\" file";
      goto END_OFF;
      }

    DEBUG(D_transport) debug_printf("%d bytes read from %s\n", cache_size, oncelog);

    /* Scan the data for this recipient. Each entry in the file starts with
    a time_t sized time value, followed by the address, followed by a binary
    zero. If we find a match, put the time into "then", and the place where it
    was found into "cache_time". Otherwise, "then" is left at zero. */

    p = cache_buff;
    while (p < cache_buff + cache_size)
      {
      uschar *s = p + sizeof(time_t);
      uschar *nextp = s + Ustrlen(s) + 1;
      if (Ustrcmp(to, s) == 0)
        {
        memcpy(&then, p, sizeof(time_t));
        cache_time = p;
        break;
        }
      p = nextp;
      }
    }

  /* Use a DBM file for the list of previous recipients. */

  else
    {
    EXIM_DATUM key_datum, result_datum;
    EXIM_DBOPEN(oncelog, O_RDWR|O_CREAT, ob->mode, &dbm_file);
    if (dbm_file == NULL)
      {
      addr->transport_return = DEFER;
      addr->message = string_sprintf("Failed to open %s file %s when sending "
        "message from %s transport: %s", EXIM_DBTYPE, oncelog, tblock->name,
        strerror(errno));
      goto END_OFF;
      }

    EXIM_DATUM_INIT(key_datum);        /* Some DBM libraries need datums */
    EXIM_DATUM_INIT(result_datum);     /* to be cleared */
    EXIM_DATUM_DATA(key_datum) = CS to;
    EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1;

    if (EXIM_DBGET(dbm_file, key_datum, result_datum))
      {
      /* If the datum size is that of a binary time, we are in the new world
      where messages are sent periodically. Otherwise the file is an old one,
      where the datum was filled with a tod_log time, which is assumed to be
      different in size. For that, only one message is ever sent. This change
      introduced at Exim 3.00. In a couple of years' time the test on the size
      can be abolished. */

      if (EXIM_DATUM_SIZE(result_datum) == sizeof(time_t))
        {
        memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t));
        }
      else then = now;
      }
    }

  /* Either "then" is set zero, if no message has yet been sent, or it
  is set to the time of the last sending. */

  if (then != 0 && (once_repeat_sec <= 0 || now - then < once_repeat_sec))
    {
    DEBUG(D_transport) debug_printf("message previously sent to %s%s\n", to,
      (once_repeat_sec > 0)? " and repeat time not reached" : "");
    log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
    if (log_fd >= 0)
      {
      uschar *ptr = log_buffer;
      sprintf(CS ptr, "%s\n  previously sent to %.200s\n", tod_stamp(tod_log), to);
      while(*ptr) ptr++;
      if(write(log_fd, log_buffer, ptr - log_buffer) != ptr-log_buffer
        || close(log_fd))
        DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
          "transport\n", logfile, tblock->name);
      }
    goto END_OFF;
    }

  DEBUG(D_transport) debug_printf("%s %s\n", (then <= 0)?
    "no previous message sent to" : "repeat time reached for", to);
  }

/* We are going to send a message. Ensure any requested file is available. */

if (file != NULL)
  {
  ff = Ufopen(file, "rb");
  if (ff == NULL && !ob->file_optional)
    {
    addr->transport_return = DEFER;
    addr->message = string_sprintf("Failed to open file %s when sending "
      "message from %s transport: %s", file, tblock->name, strerror(errno));
    return FALSE;
    }
  }

/* Make a subprocess to send the message */

pid = child_open_exim(&fd);

/* Creation of child failed; defer this delivery. */

if (pid < 0)
  {
  addr->transport_return = DEFER;
  addr->message = string_sprintf("Failed to create child process to send "
    "message from %s transport: %s", tblock->name, strerror(errno));
  DEBUG(D_transport) debug_printf("%s\n", addr->message);
  return FALSE;
  }

/* Create the message to be sent - recipients are taken from the headers,
as the -t option is used. The "headers" stuff *must* be last in case there
are newlines in it which might, if placed earlier, screw up other headers. */

f = fdopen(fd, "wb");

if (from != NULL) fprintf(f, "From: %s\n", from);
if (reply_to != NULL) fprintf(f, "Reply-To: %s\n", reply_to);
if (to != NULL) fprintf(f, "To: %s\n", to);
if (cc != NULL) fprintf(f, "Cc: %s\n", cc);
if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc);
if (subject != NULL) fprintf(f, "Subject: %s\n", subject);

/* Generate In-Reply-To from the message_id header; there should
always be one, but code defensively. */

for (h = header_list; h != NULL; h = h->next)
  if (h->type == htype_id) break;

if (h != NULL)
  {
  message_id = Ustrchr(h->text, ':') + 1;
  while (isspace(*message_id)) message_id++;
  fprintf(f, "In-Reply-To: %s", message_id);
  }

/* Generate a References header if there is at least one of Message-ID:,
References:, or In-Reply-To: (see RFC 2822). */

for (h = header_list; h != NULL; h = h->next)
  if (h->type != htype_old && strncmpic(US"References:", h->text, 11) == 0)
    break;

if (h == NULL)
  for (h = header_list; h != NULL; h = h->next)
    if (h->type != htype_old && strncmpic(US"In-Reply-To:", h->text, 12) == 0)
      break;

/* We limit the total length of references.  Although there is no fixed
limit, some systems do not like headers growing beyond recognition.
Keep the first message ID for the thread root and the last few for
the position inside the thread, up to a maximum of 12 altogether. */

if (h != NULL || message_id != NULL)
  {
  fprintf(f, "References:");
  if (h != NULL)
    {
    uschar *s, *id, *error;
    uschar *referenced_ids[12];
    int reference_count = 0;
    int i;

    s = Ustrchr(h->text, ':') + 1;
    parse_allow_group = FALSE;
    while (*s != 0 && (s = parse_message_id(s, &id, &error)) != NULL)
      {
      if (reference_count == sizeof(referenced_ids)/sizeof(uschar *))
        {
        memmove(referenced_ids + 1, referenced_ids + 2,
           sizeof(referenced_ids) - 2*sizeof(uschar *));
        referenced_ids[reference_count - 1] = id;
        }
      else referenced_ids[reference_count++] = id;
      }
    for (i = 0; i < reference_count; ++i) fprintf(f, " %s", referenced_ids[i]);
    }

  /* The message id will have a newline on the end of it. */

  if (message_id != NULL) fprintf(f, " %s", message_id);
    else fprintf(f, "\n");
  }

/* Add an Auto-Submitted: header */

fprintf(f, "Auto-Submitted: auto-replied\n");

/* Add any specially requested headers */

if (headers != NULL) fprintf(f, "%s\n", headers);
fprintf(f, "\n");

if (text != NULL)
  {
  fprintf(f, "%s", CS text);
  if (text[Ustrlen(text)-1] != '\n') fprintf(f, "\n");
  }

if (ff != NULL)
  {
  while (Ufgets(big_buffer, big_buffer_size, ff) != NULL)
    {
    if (file_expand)
      {
      uschar *s = expand_string(big_buffer);
      DEBUG(D_transport)
        {
        if (s == NULL)
          debug_printf("error while expanding line from file:\n  %s\n  %s\n",
            big_buffer, expand_string_message);
        }
      fprintf(f, "%s", (s == NULL)? CS big_buffer : CS s);
      }
    else fprintf(f, "%s", CS big_buffer);
    }
  }
Example #6
0
static int
dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
  uschar **result, uschar **errmsg, uint *do_cache)
{
int rc;
int size = 256;
int ptr = 0;
int sep = 0;
int defer_mode = PASS;
int dnssec_mode = OK;
int save_retrans = dns_retrans;
int save_retry =   dns_retry;
int type;
int failrc = FAIL;
const uschar *outsep = CUS"\n";
const uschar *outsep2 = NULL;
uschar *equals, *domain, *found;

/* Because we're the working in the search pool, we try to reclaim as much
store as possible later, so we preallocate the result here */

uschar *yield = store_get(size);

dns_record *rr;
dns_answer dnsa;
dns_scan dnss;

handle = handle;           /* Keep picky compilers happy */
filename = filename;
length = length;
do_cache = do_cache;

/* If the string starts with '>' we change the output separator.
If it's followed by ';' or ',' we set the TXT output separator. */

while (isspace(*keystring)) keystring++;
if (*keystring == '>')
  {
  outsep = keystring + 1;
  keystring += 2;
  if (*keystring == ',')
    {
    outsep2 = keystring + 1;
    keystring += 2;
    }
  else if (*keystring == ';')
    {
    outsep2 = US"";
    keystring++;
    }
  while (isspace(*keystring)) keystring++;
  }

/* Check for a modifier keyword. */

for (;;)
  {
  if (strncmpic(keystring, US"defer_", 6) == 0)
    {
    keystring += 6;
    if (strncmpic(keystring, US"strict", 6) == 0)
      { defer_mode = DEFER; keystring += 6; }
    else if (strncmpic(keystring, US"lax", 3) == 0)
      { defer_mode = PASS; keystring += 3; }
    else if (strncmpic(keystring, US"never", 5) == 0)
      { defer_mode = OK; keystring += 5; }
    else
      {
      *errmsg = US"unsupported dnsdb defer behaviour";
      return DEFER;
      }
    }
  else if (strncmpic(keystring, US"dnssec_", 7) == 0)
    {
    keystring += 7;
    if (strncmpic(keystring, US"strict", 6) == 0)
      { dnssec_mode = DEFER; keystring += 6; }
    else if (strncmpic(keystring, US"lax", 3) == 0)
      { dnssec_mode = PASS; keystring += 3; }
    else if (strncmpic(keystring, US"never", 5) == 0)
      { dnssec_mode = OK; keystring += 5; }
    else
      {
      *errmsg = US"unsupported dnsdb dnssec behaviour";
      return DEFER;
      }
    }
  else if (strncmpic(keystring, US"retrans_", 8) == 0)
    {
    int timeout_sec;
    if ((timeout_sec = readconf_readtime(keystring += 8, ',', FALSE)) <= 0)
      {
      *errmsg = US"unsupported dnsdb timeout value";
      return DEFER;
      }
    dns_retrans = timeout_sec;
    while (*keystring != ',') keystring++;
    }
  else if (strncmpic(keystring, US"retry_", 6) == 0)
    {
    int retries;
    if ((retries = (int)strtol(CCS keystring + 6, CSS &keystring, 0)) < 0)
      {
      *errmsg = US"unsupported dnsdb retry count";
      return DEFER;
      }
    dns_retry = retries;
    }
  else
    break;

  while (isspace(*keystring)) keystring++;
  if (*keystring++ != ',')
    {
    *errmsg = US"dnsdb modifier syntax error";
    return DEFER;
    }
  while (isspace(*keystring)) keystring++;
  }

/* Figure out the "type" value if it is not T_TXT.
If the keystring contains an = this must be preceded by a valid type name. */

type = T_TXT;
if ((equals = Ustrchr(keystring, '=')) != NULL)
  {
  int i, len;
  uschar *tend = equals;

  while (tend > keystring && isspace(tend[-1])) tend--;
  len = tend - keystring;

  for (i = 0; i < nelem(type_names); i++)
    if (len == Ustrlen(type_names[i]) &&
        strncmpic(keystring, US type_names[i], len) == 0)
      {
      type = type_values[i];
      break;
      }

  if (i >= nelem(type_names))
    {
    *errmsg = US"unsupported DNS record type";
    return DEFER;
    }

  keystring = equals + 1;
  while (isspace(*keystring)) keystring++;
  }

/* Initialize the resolver in case this is the first time it has been used. */

dns_init(FALSE, FALSE, dnssec_mode != OK);

/* The remainder of the string must be a list of domains. As long as the lookup
for at least one of them succeeds, we return success. Failure means that none
of them were found.

The original implementation did not support a list of domains. Adding the list
feature is compatible, except in one case: when PTR records are being looked up
for a single IPv6 address. Fortunately, we can hack in a compatibility feature
here: If the type is PTR and no list separator is specified, and the entire
remaining string is valid as an IP address, set an impossible separator so that
it is treated as one item. */

if (type == T_PTR && keystring[0] != '<' &&
    string_is_ip_address(keystring, NULL) != 0)
  sep = -1;

/* SPF strings should be concatenated without a separator, thus make
it the default if not defined (see RFC 4408 section 3.1.3).
Multiple SPF records are forbidden (section 3.1.2) but are currently
not handled specially, thus they are concatenated with \n by default.
MX priority and value are space-separated by default.
SRV and TLSA record parts are space-separated by default. */

if (!outsep2) switch(type)
  {
  case T_SPF:                         outsep2 = US"";  break;
  case T_SRV: case T_MX: case T_TLSA: outsep2 = US" "; break;
  }

/* Now scan the list and do a lookup for each item */

while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
  {
  uschar rbuffer[256];
  int searchtype = (type == T_CSA)? T_SRV :         /* record type we want */
                   (type == T_MXH)? T_MX :
                   (type == T_ZNS)? T_NS : type;

  /* If the type is PTR or CSA, we have to construct the relevant magic lookup
  key if the original is an IP address (some experimental protocols are using
  PTR records for different purposes where the key string is a host name, and
  Exim's extended CSA can be keyed by domains or IP addresses). This code for
  doing the reversal is now in a separate function. */

  if ((type == T_PTR || type == T_CSA) &&
      string_is_ip_address(domain, NULL) != 0)
    {
    dns_build_reverse(domain, rbuffer);
    domain = rbuffer;
    }

  do
    {
    DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", domain);

    /* Do the lookup and sort out the result. There are four special types that
    are handled specially: T_CSA, T_ZNS, T_ADDRESSES and T_MXH.
    The first two are handled in a special lookup function so that the facility
    could be used from other parts of the Exim code. T_ADDRESSES is handled by looping
    over the types of A lookup.  T_MXH affects only what happens later on in
    this function, but for tidiness it is handled by the "special". If the
    lookup fails, continue with the next domain. In the case of DEFER, adjust
    the final "nothing found" result, but carry on to the next domain. */

    found = domain;
#if HAVE_IPV6
    if (type == T_ADDRESSES)		/* NB cannot happen unless HAVE_IPV6 */
      {
      if (searchtype == T_ADDRESSES) searchtype = T_AAAA;
      else if (searchtype == T_AAAA) searchtype = T_A;
      rc = dns_special_lookup(&dnsa, domain, searchtype, CUSS &found);
      }
    else
#endif
      rc = dns_special_lookup(&dnsa, domain, type, CUSS &found);

    lookup_dnssec_authenticated = dnssec_mode==OK ? NULL
      : dns_is_secure(&dnsa) ? US"yes" : US"no";

    if (rc == DNS_NOMATCH || rc == DNS_NODATA) continue;
    if (  rc != DNS_SUCCEED
       || (dnssec_mode == DEFER && !dns_is_secure(&dnsa))
       )
      {
      if (defer_mode == DEFER)
	{
	dns_retrans = save_retrans;
	dns_retry = save_retry;
	dns_init(FALSE, FALSE, FALSE);			/* clr dnssec bit */
	return DEFER;					/* always defer */
	}
      if (defer_mode == PASS) failrc = DEFER;         /* defer only if all do */
      continue;                                       /* treat defer as fail */
      }


    /* Search the returned records */

    for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
         rr != NULL;
         rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
      {
      if (rr->type != searchtype) continue;

      if (*do_cache > rr->ttl)
	*do_cache = rr->ttl;

      if (type == T_A || type == T_AAAA || type == T_ADDRESSES)
        {
        dns_address *da;
        for (da = dns_address_from_rr(&dnsa, rr); da; da = da->next)
          {
          if (ptr != 0) yield = string_catn(yield, &size, &ptr, outsep, 1);
          yield = string_cat(yield, &size, &ptr, da->address);
          }
        continue;
        }

      /* Other kinds of record just have one piece of data each, but there may be
      several of them, of course. */

      if (ptr != 0) yield = string_catn(yield, &size, &ptr, outsep, 1);

      if (type == T_TXT || type == T_SPF)
        {
        if (outsep2 == NULL)
          {
          /* output only the first item of data */
          yield = string_catn(yield, &size, &ptr, (uschar *)(rr->data+1),
            (rr->data)[0]);
          }
        else
          {
          /* output all items */
          int data_offset = 0;
          while (data_offset < rr->size)
            {
            uschar chunk_len = (rr->data)[data_offset++];
            if (outsep2[0] != '\0' && data_offset != 1)
              yield = string_catn(yield, &size, &ptr, outsep2, 1);
            yield = string_catn(yield, &size, &ptr,
                             US ((rr->data)+data_offset), chunk_len);
            data_offset += chunk_len;
            }
          }
        }
      else if (type == T_TLSA)
        {
        uint8_t usage, selector, matching_type;
        uint16_t i, payload_length;
        uschar s[MAX_TLSA_EXPANDED_SIZE];
	uschar * sp = s;
        uschar * p = US rr->data;

        usage = *p++;
        selector = *p++;
        matching_type = *p++;
        /* What's left after removing the first 3 bytes above */
        payload_length = rr->size - 3;
        sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2,
		selector, *outsep2, matching_type, *outsep2);
        /* Now append the cert/identifier, one hex char at a time */
        for (i=0;
             i < payload_length && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4);
             i++)
          sp += sprintf(CS sp, "%02x", (unsigned char)p[i]);

        yield = string_cat(yield, &size, &ptr, s);
        }
      else   /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */
        {
        int priority, weight, port;
        uschar s[264];
        uschar * p = US rr->data;

	switch (type)
	  {
	  case T_MXH:
	    /* mxh ignores the priority number and includes only the hostnames */
	    GETSHORT(priority, p);
	    break;

	  case T_MX:
	    GETSHORT(priority, p);
	    sprintf(CS s, "%d%c", priority, *outsep2);
	    yield = string_cat(yield, &size, &ptr, s);
	    break;

	  case T_SRV:
	    GETSHORT(priority, p);
	    GETSHORT(weight, p);
	    GETSHORT(port, p);
	    sprintf(CS s, "%d%c%d%c%d%c", priority, *outsep2,
			      weight, *outsep2, port, *outsep2);
	    yield = string_cat(yield, &size, &ptr, s);
	    break;

	  case T_CSA:
	    /* See acl_verify_csa() for more comments about CSA. */
	    GETSHORT(priority, p);
	    GETSHORT(weight, p);
	    GETSHORT(port, p);

	    if (priority != 1) continue;      /* CSA version must be 1 */

	    /* If the CSA record we found is not the one we asked for, analyse
	    the subdomain assertions in the port field, else analyse the direct
	    authorization status in the weight field. */

	    if (Ustrcmp(found, domain) != 0)
	      {
	      if (port & 1) *s = 'X';         /* explicit authorization required */
	      else *s = '?';                  /* no subdomain assertions here */
	      }
	    else
	      {
	      if (weight < 2) *s = 'N';       /* not authorized */
	      else if (weight == 2) *s = 'Y'; /* authorized */
	      else if (weight == 3) *s = '?'; /* unauthorizable */
	      else continue;                  /* invalid */
	      }

	    s[1] = ' ';
	    yield = string_catn(yield, &size, &ptr, s, 2);
	    break;

	  default:
	    break;
	  }

        /* GETSHORT() has advanced the pointer to the target domain. */

        rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
          (DN_EXPAND_ARG4_TYPE)s, sizeof(s));

        /* If an overlong response was received, the data will have been
        truncated and dn_expand may fail. */

        if (rc < 0)
          {
          log_write(0, LOG_MAIN, "host name alias list truncated: type=%s "
            "domain=%s", dns_text_type(type), domain);
          break;
          }
        else yield = string_cat(yield, &size, &ptr, s);

	if (type == T_SOA && outsep2 != NULL)
	  {
	  unsigned long serial, refresh, retry, expire, minimum;

	  p += rc;
	  yield = string_catn(yield, &size, &ptr, outsep2, 1);

	  rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
	    (DN_EXPAND_ARG4_TYPE)s, sizeof(s));
	  if (rc < 0)
	    {
	    log_write(0, LOG_MAIN, "responsible-mailbox truncated: type=%s "
	      "domain=%s", dns_text_type(type), domain);
	    break;
	    }
	  else yield = string_cat(yield, &size, &ptr, s);

	  p += rc;
	  GETLONG(serial, p); GETLONG(refresh, p);
	  GETLONG(retry,  p); GETLONG(expire,  p); GETLONG(minimum, p);
	  sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu",
	    *outsep2, serial, *outsep2, refresh,
	    *outsep2, retry,  *outsep2, expire,  *outsep2, minimum);
	  yield = string_cat(yield, &size, &ptr, s);
	  }
        }
      }    /* Loop for list of returned records */

           /* Loop for set of A-lookup types */
    } while (type == T_ADDRESSES && searchtype != T_A);

  }        /* Loop for list of domains */

/* Reclaim unused memory */

store_reset(yield + ptr + 1);

/* If ptr == 0 we have not found anything. Otherwise, insert the terminating
zero and return the result. */

dns_retrans = save_retrans;
dns_retry = save_retry;
dns_init(FALSE, FALSE, FALSE);	/* clear the dnssec bit for getaddrbyname */

if (ptr == 0) return failrc;
yield[ptr] = 0;
*result = yield;
return OK;
}
Example #7
0
/*********************************************************************
 *
 * Function    :  parse_http_url
 *
 * Description :  Parse out the host and port from the URL.  Find the
 *                hostname & path, port (if ':'), and/or password (if '@')
 *
 * Parameters  :
 *          1  :  url = URL (or is it URI?) to break down
 *          2  :  http = pointer to the http structure to hold elements.
 *                       Must be initialized with valid values (like NULLs).
 *          3  :  require_protocol = Whether or not URLs without
 *                                   protocol are acceptable.
 *
 * Returns     :  JB_ERR_OK on success
 *                JB_ERR_PARSE on malformed command/URL
 *                             or >100 domains deep.
 *
 *********************************************************************/
jb_err parse_http_url(const char *url, struct http_request *http, int require_protocol)
{
   int host_available = 1; /* A proxy can dream. */

   /*
    * Save our initial URL
    */
   http->url = strdup_or_die(url);

   /*
    * Check for * URI. If found, we're done.
    */
   if (*http->url == '*')
   {
      http->path = strdup_or_die("*");
      http->hostport = strdup_or_die("");
      if (http->url[1] != '\0')
      {
         return JB_ERR_PARSE;
      }
      return JB_ERR_OK;
   }


   /*
    * Split URL into protocol,hostport,path.
    */
   {
      char *buf;
      char *url_noproto;
      char *url_path;

      buf = strdup_or_die(url);

      /* Find the start of the URL in our scratch space */
      url_noproto = buf;
      if (strncmpic(url_noproto, "http://",  7) == 0)
      {
         url_noproto += 7;
      }
      else if (strncmpic(url_noproto, "https://", 8) == 0)
      {
         /*
          * Should only happen when called from cgi_show_url_info().
          */
         url_noproto += 8;
         http->ssl = 1;
      }
      else if (*url_noproto == '/')
      {
        /*
         * Short request line without protocol and host.
         * Most likely because the client's request
         * was intercepted and redirected into Privoxy.
         */
         http->host = NULL;
         host_available = 0;
      }
      else if (require_protocol)
      {
         freez(buf);
         return JB_ERR_PARSE;
      }

      url_path = strchr(url_noproto, '/');
      if (url_path != NULL)
      {
         /*
          * Got a path.
          *
          * NOTE: The following line ignores the path for HTTPS URLS.
          * This means that you get consistent behaviour if you type a
          * https URL in and it's parsed by the function.  (When the
          * URL is actually retrieved, SSL hides the path part).
          */
         http->path = strdup_or_die(http->ssl ? "/" : url_path);
         *url_path = '\0';
         http->hostport = strdup_or_die(url_noproto);
      }
      else
      {
         /*
          * Repair broken HTTP requests that don't contain a path,
          * or CONNECT requests
          */
         http->path = strdup_or_die("/");
         http->hostport = strdup_or_die(url_noproto);
      }

      freez(buf);
   }

   if (!host_available)
   {
      /* Without host, there is nothing left to do here */
      return JB_ERR_OK;
   }

   /*
    * Split hostport into user/password (ignored), host, port.
    */
   {
      char *buf;
      char *host;
      char *port;

      buf = strdup_or_die(http->hostport);

      /* check if url contains username and/or password */
      host = strchr(buf, '@');
      if (host != NULL)
      {
         /* Contains username/password, skip it and the @ sign. */
         host++;
      }
      else
      {
         /* No username or password. */
         host = buf;
      }

      /* Move after hostname before port number */
      if (*host == '[')
      {
         /* Numeric IPv6 address delimited by brackets */
         host++;
         port = strchr(host, ']');

         if (port == NULL)
         {
            /* Missing closing bracket */
            freez(buf);
            return JB_ERR_PARSE;
         }

         *port++ = '\0';

         if (*port == '\0')
         {
            port = NULL;
         }
         else if (*port != ':')
         {
            /* Garbage after closing bracket */
            freez(buf);
            return JB_ERR_PARSE;
         }
      }
      else
      {
         /* Plain non-escaped hostname */
         port = strchr(host, ':');
      }

      /* check if url contains port */
      if (port != NULL)
      {
         /* Contains port */
         char *endptr;
         long parsed_port;
         /* Terminate hostname and point to start of port string */
         *port++ = '\0';
         parsed_port = strtol(port, &endptr, 10);
         if ((parsed_port <= 0) || (parsed_port > 65535) || (*endptr != '\0'))
         {
            log_error(LOG_LEVEL_ERROR, "Invalid port in URL: %s.", url);
            freez(buf);
            return JB_ERR_PARSE;
         }
         http->port = (int)parsed_port;
      }
      else
      {
         /* No port specified. */
         http->port = (http->ssl ? 443 : 80);
      }

      http->host = strdup_or_die(host);

      freez(buf);
   }

#ifdef FEATURE_EXTENDED_HOST_PATTERNS
   return JB_ERR_OK;
#else
   /* Split domain name so we can compare it against wildcards */
   return init_domain_components(http);
#endif /* def FEATURE_EXTENDED_HOST_PATTERNS */

}
Example #8
0
File: mime.c Project: akissa/exim
int
mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *context,
    uschar **user_msgptr, uschar **log_msgptr)
{
int rc = OK;
uschar * header = NULL;
struct mime_boundary_context nested_context;

/* reserve a line buffer to work in */
header = store_get(MIME_MAX_HEADER_SIZE+1);

/* Not actually used at the moment, but will be vital to fixing
 * some RFC 2046 nonconformance later... */
nested_context.parent = context;

/* loop through parts */
while(1)
  {
  /* reset all per-part mime variables */
  mime_vars_reset();

  /* If boundary is null, we assume that *f is positioned on the start of
  headers (for example, at the very beginning of a message.  If a boundary is
  given, we must first advance to it to reach the start of the next header
  block.  */

  /* NOTE -- there's an error here -- RFC2046 specifically says to
   * check for outer boundaries.  This code doesn't do that, and
   * I haven't fixed this.
   *
   * (I have moved partway towards adding support, however, by adding
   * a "parent" field to my new boundary-context structure.)
   */
  if (context) for (;;)
    {
    if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f))
      {
      /* Hit EOF or read error. Ugh. */
      DEBUG(D_acl) debug_printf("MIME: Hit EOF ...\n");
      return rc;
      }

    /* boundary line must start with 2 dashes */
    if (  Ustrncmp(header, "--", 2) == 0
       && Ustrncmp(header+2, context->boundary, Ustrlen(context->boundary)) == 0
       )
      {			/* found boundary */
      if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0)
	{
	/* END boundary found */
	DEBUG(D_acl) debug_printf("MIME: End boundary found %s\n",
	  context->boundary);
	return rc;
	}

      DEBUG(D_acl) debug_printf("MIME: Next part with boundary %s\n",
	context->boundary);
      break;
      }
    }

  /* parse headers, set up expansion variables */
  while (mime_get_header(f, header))
    {
    struct mime_header * mh;

    /* look for interesting headers */
    for (mh = mime_header_list;
	 mh < mime_header_list + mime_header_list_size;
	 mh++) if (strncmpic(mh->name, header, mh->namelen) == 0)
      {
      uschar * p = header + mh->namelen;
      uschar * q;

      /* grab the value (normalize to lower case)
      and copy to its corresponding expansion variable */

      for (q = p; *q != ';' && *q; q++) ;
      *mh->value = string_copynlc(p, q-p);
      DEBUG(D_acl) debug_printf("MIME: found %s header, value is '%s'\n",
	mh->name, *mh->value);

      if (*(p = q)) p++;			/* jump past the ; */

	{
	uschar * mime_fname = NULL;
	uschar * mime_fname_rfc2231 = NULL;
	uschar * mime_filename_charset = NULL;
	BOOL decoding_failed = FALSE;

	/* grab all param=value tags on the remaining line,
	check if they are interesting */

	while (*p)
	  {
	  mime_parameter * mp;

	  DEBUG(D_acl) debug_printf("MIME:   considering paramlist '%s'\n", p);

	  if (  !mime_filename
	     && strncmpic(CUS"content-disposition:", header, 20) == 0
	     && strncmpic(CUS"filename*", p, 9) == 0
	     )
	    {					/* RFC 2231 filename */
	    uschar * q;

	    /* find value of the filename */
	    p += 9;
	    while(*p != '=' && *p) p++;
	    if (*p) p++;			/* p is filename or NUL */
	    q = mime_param_val(&p);		/* p now trailing ; or NUL */

	    if (q && *q)
	      {
	      uschar * temp_string, * err_msg;
	      int slen;

	      /* build up an un-decoded filename over successive
	      filename*= parameters (for use when 2047 decode fails) */

	      mime_fname_rfc2231 = string_sprintf("%#s%s",
		mime_fname_rfc2231, q);

	      if (!decoding_failed)
		{
		int size;
		if (!mime_filename_charset)
		  {
		  uschar * s = q;

		  /* look for a ' in the "filename" */
		  while(*s != '\'' && *s) s++;	/* s is 1st ' or NUL */

		  if ((size = s-q) > 0)
		    mime_filename_charset = string_copyn(q, size);

		  if (*(p = s)) p++;
		  while(*p == '\'') p++;	/* p is after 2nd ' */
		  }
		else
		  p = q;

		DEBUG(D_acl) debug_printf("MIME:    charset %s fname '%s'\n",
		  mime_filename_charset ? mime_filename_charset : US"<NULL>", p);

		temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen);
		DEBUG(D_acl) debug_printf("MIME:    2047-name %s\n", temp_string);

		temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ',
		  NULL, &err_msg);
		DEBUG(D_acl) debug_printf("MIME:    plain-name %s\n", temp_string);

		size = Ustrlen(temp_string);

		if (size == slen)
		  decoding_failed = TRUE;
		else
		  /* build up a decoded filename over successive
		  filename*= parameters */

		  mime_filename = mime_fname = mime_fname
		    ? string_sprintf("%s%s", mime_fname, temp_string)
		    : temp_string;
		}
	      }
	    }

	  else
	    /* look for interesting parameters */
	    for (mp = mime_parameter_list;
		 mp < mime_parameter_list + nelem(mime_parameter_list);
		 mp++
		) if (strncmpic(mp->name, p, mp->namelen) == 0)
	      {
	      uschar * q;
	      uschar * dummy_errstr;

	      /* grab the value and copy to its expansion variable */
	      p += mp->namelen;
	      q = mime_param_val(&p);		/* p now trailing ; or NUL */

	      *mp->value = q && *q
		? rfc2047_decode(q, check_rfc2047_length, NULL, 32, NULL,
		    &dummy_errstr)
		: NULL;
	      DEBUG(D_acl) debug_printf(
		"MIME:  found %s parameter in %s header, value '%s'\n",
		mp->name, mh->name, *mp->value);

	      break;			/* done matching param names */
	      }


	  /* There is something, but not one of our interesting parameters.
	     Advance past the next semicolon */
	  p = mime_next_semicolon(p);
	  if (*p) p++;
	  }				/* param scan on line */

	if (strncmpic(CUS"content-disposition:", header, 20) == 0)
	  {
	  if (decoding_failed) mime_filename = mime_fname_rfc2231;

	  DEBUG(D_acl) debug_printf(
	    "MIME:  found %s parameter in %s header, value is '%s'\n",
	    "filename", mh->name, mime_filename);
	  }
	}
      }
    }

  /* set additional flag variables (easier access) */
  if (  mime_content_type
     && Ustrncmp(mime_content_type,"multipart",9) == 0
     )
    mime_is_multipart = 1;

  /* Make a copy of the boundary pointer.
     Required since mime_boundary is global
     and can be overwritten further down in recursion */
  nested_context.boundary = mime_boundary;

  /* raise global counter */
  mime_part_count++;

  /* copy current file handle to global variable */
  mime_stream = f;
  mime_current_boundary = context ? context->boundary : 0;

  /* Note the context */
  mime_is_coverletter = !(context && context->context == MBC_ATTACHMENT);

  /* call ACL handling function */
  rc = acl_check(ACL_WHERE_MIME, NULL, acl, user_msgptr, log_msgptr);

  mime_stream = NULL;
  mime_current_boundary = NULL;

  if (rc != OK) break;

  /* If we have a multipart entity and a boundary, go recursive */
  if ( (mime_content_type != NULL) &&
       (nested_context.boundary != NULL) &&
       (Ustrncmp(mime_content_type,"multipart",9) == 0) )
    {
    DEBUG(D_acl)
      debug_printf("MIME: Entering multipart recursion, boundary '%s'\n",
	nested_context.boundary);

    nested_context.context =
      context && context->context == MBC_ATTACHMENT
      ? MBC_ATTACHMENT
      :    Ustrcmp(mime_content_type,"multipart/alternative") == 0
	|| Ustrcmp(mime_content_type,"multipart/related") == 0
      ? MBC_COVERLETTER_ALL
      : MBC_COVERLETTER_ONESHOT;

    rc = mime_acl_check(acl, f, &nested_context, user_msgptr, log_msgptr);
    if (rc != OK) break;
    }
  else if ( (mime_content_type != NULL) &&
	  (Ustrncmp(mime_content_type,"message/rfc822",14) == 0) )
    {
    const uschar *rfc822name = NULL;
    uschar filename[2048];
    int file_nr = 0;
    int result = 0;

    /* must find first free sequential filename */
    do
      {
      struct stat mystat;
      (void)string_format(filename, 2048,
	"%s/scan/%s/__rfc822_%05u", spool_directory, message_id, file_nr++);
      /* security break */
      if (file_nr >= 128)
	goto NO_RFC822;
      result = stat(CS filename,&mystat);
      } while (result != -1);

    rfc822name = filename;

    /* decode RFC822 attachment */
    mime_decoded_filename = NULL;
    mime_stream = f;
    mime_current_boundary = context ? context->boundary : NULL;
    mime_decode(&rfc822name);
    mime_stream = NULL;
    mime_current_boundary = NULL;
    if (!mime_decoded_filename)		/* decoding failed */
      {
      log_write(0, LOG_MAIN,
	   "mime_regex acl condition warning - could not decode RFC822 MIME part to file.");
      rc = DEFER;
      goto out;
      }
    mime_decoded_filename = NULL;
    }

NO_RFC822:
  /* If the boundary of this instance is NULL, we are finished here */
  if (!context) break;

  if (context->context == MBC_COVERLETTER_ONESHOT)
    context->context = MBC_ATTACHMENT;
  }

out:
mime_vars_reset();
return rc;
}
Example #9
0
static int
internal_lsearch_find(void *handle, uschar *filename, uschar *keystring,
  int length, uschar **result, uschar **errmsg, int type)
{
FILE *f = (FILE *)handle;
BOOL last_was_eol = TRUE;
BOOL this_is_eol = TRUE;
int old_pool = store_pool;
void *reset_point = NULL;
uschar buffer[4096];

/* Wildcard searches may use up some store, because of expansions. We don't
want them to fill up our search store. What we do is set the pool to the main
pool and get a point to reset to later. Wildcard searches could also issue
lookups, but internal_search_find will take care of that, and the cache will be
safely stored in the search pool again. */

if(type == LSEARCH_WILD || type == LSEARCH_NWILD)
  {
  store_pool = POOL_MAIN;
  reset_point = store_get(0);
  }

filename = filename;  /* Keep picky compilers happy */
errmsg = errmsg;

rewind(f);
for (last_was_eol = TRUE;
     Ufgets(buffer, sizeof(buffer), f) != NULL;
     last_was_eol = this_is_eol)
  {
  int ptr, size;
  int p = Ustrlen(buffer);
  int linekeylength;
  BOOL this_is_comment;
  uschar *yield;
  uschar *s = buffer;

  /* Check whether this the final segment of a line. If it follows an
  incomplete part-line, skip it. */

  this_is_eol = p > 0 && buffer[p-1] == '\n';
  if (!last_was_eol) continue;

  /* We now have the start of a physical line. If this is a final line segment,
  remove trailing white space. */

  if (this_is_eol)
    {
    while (p > 0 && isspace((uschar)buffer[p-1])) p--;
    buffer[p] = 0;
    }

  /* If the buffer is empty it might be (a) a complete empty line, or (b) the
  start of a line that begins with so much white space that it doesn't all fit
  in the buffer. In both cases we want to skip the entire physical line.

  If the buffer begins with # it is a comment line; if it begins with white
  space it is a logical continuation; again, we want to skip the entire
  physical line. */

  if (buffer[0] == 0 || buffer[0] == '#' || isspace(buffer[0])) continue;

  /* We assume that they key will fit in the buffer. If the key starts with ",
  read it as a quoted string. We don't use string_dequote() because that uses
  new store for the result, and we may be doing this many times in a long file.
  We know that the dequoted string must be shorter than the original, because
  we are removing the quotes, and also any escape sequences always turn two or
  more characters into one character. Therefore, we can store the new string in
  the same buffer. */

  if (*s == '\"')
    {
    uschar *t = s++;
    while (*s != 0 && *s != '\"')
      {
      if (*s == '\\') *t++ = string_interpret_escape(&s);
        else *t++ = *s;
      s++;
      }
    if (*s != 0) s++;               /* Past terminating " */
    linekeylength = t - buffer;
    }

  /* Otherwise it is terminated by a colon or white space */

  else
    {
    while (*s != 0 && *s != ':' && !isspace(*s)) s++;
    linekeylength = s - buffer;
    }

  /* The matching test depends on which kind of lsearch we are doing */

  switch(type)
    {
    /* A plain lsearch treats each key as a literal */

    case LSEARCH_PLAIN:
    if (linekeylength != length || strncmpic(buffer, keystring, length) != 0)
      continue;
    break;      /* Key matched */

    /* A wild lsearch treats each key as a possible wildcarded string; no
    expansion is done for nwildlsearch. */

    case LSEARCH_WILD:
    case LSEARCH_NWILD:
      {
      int rc;
      int save = buffer[linekeylength];
      uschar *list = buffer;
      buffer[linekeylength] = 0;
      rc = match_isinlist(keystring,
        &list,
        UCHAR_MAX+1,              /* Single-item list */
        NULL,                     /* No anchor */
        NULL,                     /* No caching */
        MCL_STRING + ((type == LSEARCH_WILD)? 0:MCL_NOEXPAND),
        TRUE,                     /* Caseless */
        NULL);
      buffer[linekeylength] = save;
      if (rc == FAIL) continue;
      if (rc == DEFER) return DEFER;
      }

    /* The key has matched. If the search involved a regular expression, it
    might have caused numerical variables to be set. However, their values will
    be in the wrong storage pool for external use. Copying them to the standard
    pool is not feasible because of the caching of lookup results - a repeated
    lookup will not match the regular expression again. Therefore, we flatten
    all numeric variables at this point. */

    expand_nmax = -1;
    break;

    /* Compare an ip address against a list of network/ip addresses. We have to
    allow for the "*" case specially. */

    case LSEARCH_IP:
    if (linekeylength == 1 && buffer[0] == '*')
      {
      if (length != 1 || keystring[0] != '*') continue;
      }
    else if (length == 1 && keystring[0] == '*') continue;
    else
      {
      int maskoffset;
      int save = buffer[linekeylength];
      buffer[linekeylength] = 0;
      if (string_is_ip_address(buffer, &maskoffset) == 0 ||
          !host_is_in_net(keystring, buffer, maskoffset)) continue;
      buffer[linekeylength] = save;
      }
    break;      /* Key matched */
    }

  /* The key has matched. Skip spaces after the key, and allow an optional
  colon after the spaces. This is an odd specification, but it's for
  compatibility. */

  while (isspace((uschar)*s)) s++;
  if (*s == ':')
    {
    s++;
    while (isspace((uschar)*s)) s++;
    }

  /* Reset dynamic store, if we need to, and revert to the search pool */

  if (reset_point != NULL)
    {
    store_reset(reset_point);
    store_pool = old_pool;
    }

  /* Now we want to build the result string to contain the data. There can be
  two kinds of continuation: (a) the physical line may not all have fitted into
  the buffer, and (b) there may be logical continuation lines, for which we
  must convert all leading white space into a single blank.

  Initialize, and copy the first segment of data. */

  this_is_comment = FALSE;
  size = 100;
  ptr = 0;
  yield = store_get(size);
  if (*s != 0)
    yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));

  /* Now handle continuations */

  for (last_was_eol = this_is_eol;
       Ufgets(buffer, sizeof(buffer), f) != NULL;
       last_was_eol = this_is_eol)
    {
    s = buffer;
    p = Ustrlen(buffer);
    this_is_eol = p > 0 && buffer[p-1] == '\n';

    /* Remove trailing white space from a physical line end */

    if (this_is_eol)
      {
      while (p > 0 && isspace((uschar)buffer[p-1])) p--;
      buffer[p] = 0;
      }

    /* If this is not a physical line continuation, skip it entirely if it's
    empty or starts with #. Otherwise, break the loop if it doesn't start with
    white space. Otherwise, replace leading white space with a single blank. */

    if (last_was_eol)
      {
      this_is_comment = (this_is_comment || (buffer[0] == 0 || buffer[0] == '#'));
      if (this_is_comment) continue;
      if (!isspace((uschar)buffer[0])) break;
      while (isspace((uschar)*s)) s++;
      *(--s) = ' ';
      }
    if (this_is_comment) continue;

    /* Join a physical or logical line continuation onto the result string. */

    yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
    }

  yield[ptr] = 0;
  store_reset(yield + ptr + 1);
  *result = yield;
  return OK;
  }

/* Reset dynamic store, if we need to */

if (reset_point != NULL)
  {
  store_reset(reset_point);
  store_pool = old_pool;
  }

return FAIL;
}
Example #10
0
/*********************************************************************
 *
 * Function    :  block_url
 *
 * Description :  Called from `chat'.  Check to see if we need to block this.
 *
 * Parameters  :
 *          1  :  csp = Current client state (buffers, headers, etc...)
 *
 * Returns     :  NULL => unblocked, else HTTP block response
 *
 *********************************************************************/
struct http_response *block_url(struct client_state *csp)
{
#ifdef FEATURE_IMAGE_BLOCKING
   char *p;
#endif /* def FEATURE_IMAGE_BLOCKING */
   struct http_response *rsp;

   /*
    * If it's not blocked, don't block it ;-)
    */
   if ((csp->action->flags & ACTION_BLOCK) == 0)
   {
      return NULL;
   }

   /*
    * Else, prepare a response
    */
   if (NULL == (rsp = alloc_http_response()))
   {
      return cgi_error_memory();
   }

   /*
    * If it's an image-url, send back an image or redirect
    * as specified by the relevant +image action
    */
#ifdef FEATURE_IMAGE_BLOCKING
   if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
        && is_imageurl(csp))
   {
      /* determine HOW images should be blocked */
      p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];

#if 1 /* Two alternative strategies, use this one for now: */

      /* and handle accordingly: */
      if ((p == NULL) || (0 == strcmpic(p, "pattern")))
      {
         rsp->body = bindup(image_pattern_data, image_pattern_length);
         if (rsp->body == NULL)
         {
            free_http_response(rsp);
            return cgi_error_memory();
         }
         rsp->content_length = image_pattern_length;

         if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
         {
            free_http_response(rsp);
            return cgi_error_memory();
         }
      }

      else if (0 == strcmpic(p, "blank"))
      {
         rsp->body = bindup(image_blank_data, image_blank_length);
         if (rsp->body == NULL)
         {
            free_http_response(rsp);
            return cgi_error_memory();
         }
         rsp->content_length = image_blank_length;

         if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
         {
            free_http_response(rsp);
            return cgi_error_memory();
         }
      }

      else
      {
         rsp->status = strdup("302 Local Redirect from Privoxy");
         if (rsp->status == NULL)
         {
            free_http_response(rsp);
            return cgi_error_memory();
         }

         if (enlist_unique_header(rsp->headers, "Location", p))
         {
            free_http_response(rsp);
            return cgi_error_memory();
         }
      }

#else /* Following code is disabled for now */

      /* and handle accordingly: */
      if ((p == NULL) || (0 == strcmpic(p, "pattern")))
      {
         p = CGI_PREFIX "send-banner?type=pattern";
      }
      else if (0 == strcmpic(p, "blank"))
      {
         p = CGI_PREFIX "send-banner?type=blank";
      }
      rsp->status = strdup("302 Local Redirect from Privoxy");
      if (rsp->status == NULL)
      {
         free_http_response(rsp);
         return cgi_error_memory();
      }

      if (enlist_unique_header(rsp->headers, "Location", p))
      {
         free_http_response(rsp);
         return cgi_error_memory();
      }
#endif /* Preceeding code is disabled for now */
   }
   else
#endif /* def FEATURE_IMAGE_BLOCKING */

   /*
    * Else, generate an HTML "blocked" message:
    */
   {
      jb_err err;
      struct map * exports;

      /*
       * Workaround for stupid Netscape bug which prevents
       * pages from being displayed if loading a referenced
       * JavaScript or style sheet fails. So make it appear
       * as if it succeeded.
       */
      if ( NULL != (p = get_header_value(csp->headers, "User-Agent:"))
           && !strncmpic(p, "mozilla", 7) /* Catch Netscape but */
           && !strstr(p, "Gecko")         /* save Mozilla, */
           && !strstr(p, "compatible")    /* MSIE */
           && !strstr(p, "Opera"))        /* and Opera. */
      {
         rsp->status = strdup("200 Request for blocked URL");
      }
      else
      {
         rsp->status = strdup("404 Request for blocked URL");
      }

      if (rsp->status == NULL)
      {
         free_http_response(rsp);
         return cgi_error_memory();
      }

      exports = default_exports(csp, NULL);
      if (exports == NULL)
      {
         free_http_response(rsp);
         return cgi_error_memory();
      }

#ifdef FEATURE_FORCE_LOAD
      err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
      if (csp->http->ssl != 0)
#endif /* ndef FEATURE_FORCE_LOAD */
      {
         err = map_block_killer(exports, "force-support");
      }

      if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
      if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
      if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
      if (!err) err = map(exports, "path-ue", 1, url_encode(csp->http->path), 0);

      if (err)
      {
         free_map(exports);
         free_http_response(rsp);
         return cgi_error_memory();
      }

      err = template_fill_for_cgi(csp, "blocked", exports, rsp);
      if (err)
      {
         free_http_response(rsp);
         return cgi_error_memory();
      }
   }

   return finish_http_response(rsp);

}
Example #11
0
File: ldap.c Project: fanf2/exim
static int
control_ldap_search(uschar *ldap_url, int search_type, uschar **res,
  uschar **errmsg)
{
BOOL defer_break = FALSE;
int timelimit = LDAP_NO_LIMIT;
int sizelimit = LDAP_NO_LIMIT;
int tcplimit = 0;
int sep = 0;
int dereference = LDAP_DEREF_NEVER;
void* referrals = LDAP_OPT_ON;
uschar *url = ldap_url;
uschar *p;
uschar *user = NULL;
uschar *password = NULL;
uschar *server, *list;
uschar buffer[512];

while (isspace(*url)) url++;

/* Until the string begins "ldap", search for the other parameter settings that
are recognized. They are of the form NAME=VALUE, with the value being
optionally double-quoted. There must still be a space after it, however. No
NAME has the value "ldap". */

while (strncmpic(url, US"ldap", 4) != 0)
  {
  uschar *name = url;
  while (*url != 0 && *url != '=') url++;
  if (*url == '=')
    {
    int namelen;
    uschar *value;
    namelen = ++url - name;
    value = string_dequote(&url);
    if (isspace(*url))
      {
      if (strncmpic(name, US"USER="******"PASS="******"SIZE=", namelen) == 0) sizelimit = Uatoi(value);
      else if (strncmpic(name, US"TIME=", namelen) == 0) timelimit = Uatoi(value);
      else if (strncmpic(name, US"CONNECT=", namelen) == 0) tcplimit = Uatoi(value);
      else if (strncmpic(name, US"NETTIME=", namelen) == 0) tcplimit = Uatoi(value);

      /* Don't know if all LDAP libraries have LDAP_OPT_DEREF */

      #ifdef LDAP_OPT_DEREF
      else if (strncmpic(name, US"DEREFERENCE=", namelen) == 0)
        {
        if (strcmpic(value, US"never") == 0) dereference = LDAP_DEREF_NEVER;
        else if (strcmpic(value, US"searching") == 0)
          dereference = LDAP_DEREF_SEARCHING;
        else if (strcmpic(value, US"finding") == 0)
          dereference = LDAP_DEREF_FINDING;
        if (strcmpic(value, US"always") == 0) dereference = LDAP_DEREF_ALWAYS;
        }
      #else
      else if (strncmpic(name, US"DEREFERENCE=", namelen) == 0)
        {
        *errmsg = string_sprintf("LDAP_OP_DEREF not defined in this LDAP "
          "library - cannot use \"dereference\"");
        DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
        return DEFER;
        }
      #endif

      #ifdef LDAP_OPT_REFERRALS
      else if (strncmpic(name, US"REFERRALS=", namelen) == 0)
        {
        if (strcmpic(value, US"follow") == 0) referrals = LDAP_OPT_ON;
        else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF;
        else
          {
          *errmsg = string_sprintf("LDAP option REFERRALS is not \"follow\" "
            "or \"nofollow\"");
          DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
          return DEFER;
          }
        }
      #else
      else if (strncmpic(name, US"REFERRALS=", namelen) == 0)
        {
        *errmsg = string_sprintf("LDAP_OP_REFERRALS not defined in this LDAP "
          "library - cannot use \"referrals\"");
        DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
        return DEFER;
        }
      #endif

      else
        {
        *errmsg =
          string_sprintf("unknown parameter \"%.*s\" precedes LDAP URL",
            namelen, name);
        DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
        return DEFER;
        }
      while (isspace(*url)) url++;
      continue;
      }
    }
  *errmsg = US"malformed parameter setting precedes LDAP URL";
  DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
  return DEFER;
  }

/* If user is set, de-URL-quote it. Some LDAP libraries do this for themselves,
but it seems that not all behave like this. The DN for the user is often the
result of ${quote_ldap_dn:...} quoting, which does apply URL quoting, because
that is needed when the DN is used as a base DN in a query. Sigh. This is all
far too complicated. */

if (user != NULL)
  {
  uschar *s;
  uschar *t = user;
  for (s = user; *s != 0; s++)
    {
    int c, d;
    if (*s == '%' && isxdigit(c=s[1]) && isxdigit(d=s[2]))
      {
      c = tolower(c);
      d = tolower(d);
      *t++ =
        (((c >= 'a')? (10 + c - 'a') : c - '0') << 4) |
         ((d >= 'a')? (10 + d - 'a') : d - '0');
      s += 2;
      }
    else *t++ = *s;
    }
  *t = 0;
  }

DEBUG(D_lookup)
  debug_printf("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d "
    "dereference=%d referrals=%s\n", user, password, sizelimit, timelimit,
    tcplimit, dereference, (referrals == LDAP_OPT_ON)? "on" : "off");

/* If the request is just to check authentication, some credentials must
be given. The password must not be empty because LDAP binds with an empty
password are considered anonymous, and will succeed on most installations. */

if (search_type == SEARCH_LDAP_AUTH)
  {
  if (user == NULL || password == NULL)
    {
    *errmsg = US"ldapauth lookups must specify the username and password";
    return DEFER;
    }
  if (password[0] == 0)
    {
    DEBUG(D_lookup) debug_printf("Empty password: ldapauth returns FAIL\n");
    return FAIL;
    }
  }

/* Check for valid ldap url starters */

p = url + 4;
if (tolower(*p) == 's' || tolower(*p) == 'i') p++;
if (Ustrncmp(p, "://", 3) != 0)
  {
  *errmsg = string_sprintf("LDAP URL does not start with \"ldap://\", "
    "\"ldaps://\", or \"ldapi://\" (it starts with \"%.16s...\")", url);
  DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
  return DEFER;
  }

/* No default servers, or URL contains a server name: just one attempt */

if (eldap_default_servers == NULL || p[3] != '/')
  {
  return perform_ldap_search(url, NULL, 0, search_type, res, errmsg,
    &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference,
    referrals);
  }

/* Loop through the default servers until OK or FAIL */

list = eldap_default_servers;
while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
  {
  int rc;
  int port = 0;
  uschar *colon = Ustrchr(server, ':');
  if (colon != NULL)
    {
    *colon = 0;
    port = Uatoi(colon+1);
    }
  rc = perform_ldap_search(url, server, port, search_type, res, errmsg,
    &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference,
    referrals);
  if (rc != DEFER || defer_break) return rc;
  }

return DEFER;
}
Example #12
0
int
ipliteral_router_entry(
  router_instance *rblock,        /* data for this instantiation */
  address_item *addr,             /* address we are working on */
  struct passwd *pw,              /* passwd entry after check_local_user */
  int verify,                     /* v_none/v_recipient/v_sender/v_expn */
  address_item **addr_local,      /* add it to this if it's local */
  address_item **addr_remote,     /* add it to this if it's remote */
  address_item **addr_new,        /* put new addresses on here */
  address_item **addr_succeed)    /* put old address here on success */
{
/*
ipliteral_router_options_block *ob =
  (ipliteral_router_options_block *)(rblock->options_block);
*/
host_item *h;
const uschar *domain = addr->domain;
const uschar *ip;
int len = Ustrlen(domain);
int rc, ipv;

addr_new = addr_new;         /* Keep picky compilers happy */
addr_succeed = addr_succeed;

DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
  rblock->name, addr->address, addr->domain);

/* Check that the domain is an IP address enclosed in square brackets. Remember
to allow for the "official" form of IPv6 addresses. If not, the router
declines. Otherwise route to the single IP address, setting the host name to
"(unnamed)". */

if (domain[0] != '[' || domain[len-1] != ']') return DECLINE;
ip = string_copyn(domain+1, len-2);
if (strncmpic(ip, US"IPV6:", 5) == 0 || strncmpic(ip, US"IPV4:", 5) == 0)
  ip += 5;

ipv = string_is_ip_address(ip, NULL);
if (ipv == 0 || (disable_ipv6 && ipv == 6))
  return DECLINE;

/* It seems unlikely that ignore_target_hosts will be used with this router,
but if it is set, it should probably work. */

if (verify_check_this_host(CUSS&rblock->ignore_target_hosts,
       	NULL, domain, ip, NULL) == OK)
  {
  DEBUG(D_route)
      debug_printf("%s is in ignore_target_hosts\n", ip);
  addr->message = US"IP literal host explicitly ignored";
  return DECLINE;
  }

/* Set up a host item */

h = store_get(sizeof(host_item));

h->next = NULL;
h->address = string_copy(ip);
h->port = PORT_NONE;
h->name = domain;
h->mx = MX_NONE;
h->status = hstatus_unknown;
h->why = hwhy_unknown;
h->last_try = 0;

/* Determine whether the host is the local host, and if so, take action
according to the configuration. */

if (host_scan_for_local_hosts(h, &h, NULL) == HOST_FOUND_LOCAL)
  {
  int rc = rf_self_action(addr, h, rblock->self_code, rblock->self_rewrite,
    rblock->self, addr_new);
  if (rc != OK) return rc;
  }

/* Address is routed to this host */

addr->host_list = h;

/* Set up the errors address, if any. */

rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address);
if (rc != OK) return rc;

/* Set up the additional and removable headers for this address. */

rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers,
  &addr->prop.remove_headers);
if (rc != OK) return rc;

/* Fill in the transport, queue the address for local or remote delivery, and
yield success. For local delivery, of course, the IP address won't be used. If
just verifying, there need not be a transport, in which case it doesn't matter
which queue we put the address on. This is all now handled by the route_queue()
function. */

if (!rf_get_transport(rblock->transport_name, &(rblock->transport),
      addr, rblock->name, NULL))
  return DEFER;

addr->transport = rblock->transport;

return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
  OK : DEFER;
}