Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postfix/src/util/   (Postfix Mailserver Version 3.11©)  Datei vom 10.11.2012 mit Größe 17 kB image not shown  

Quelle  ip_match.c

  Sprache: C
 

/*++
/* NAME
/* ip_match 3
/* SUMMARY
/* IP address pattern matching
/* SYNOPSIS
/* #include <ip_match.h>
/*
/* char *ip_match_parse(byte_codes, pattern)
/* VSTRING *byte_codes;
/* char *pattern;
/*
/* char *ip_match_save(byte_codes)
/* const VSTRING *byte_codes;
/*
/* int ip_match_execute(byte_codes, addr_bytes)
/* cost char *byte_codes;
/* const char *addr_bytes;
/*
/* char *ip_match_dump(printable, byte_codes)
/* VSTRING *printable;
/* const char *byte_codes;
/* DESCRIPTION
/* This module supports IP address pattern matching. See below
/* for a description of the supported address pattern syntax.
/*
/* This implementation aims to minimize the cost of encoding
/* the pattern in internal form, while still providing good
/* matching performance in the typical case.   The first byte
/* of an encoded pattern specifies the expected address family
/* (for example, AF_INET); other details of the encoding are
/* private and are subject to change.
/*
/* ip_match_parse() converts the user-specified pattern to
/* internal form. The result value is a null pointer in case
/* of success, or a pointer into the byte_codes buffer with a
/* detailed problem description.
/*
/* ip_match_save() saves the result from ip_match_parse() for
/* longer-term usage. The result should be passed to myfree().
/*
/* ip_match_execute() matches a binary network in addr_bytes
/* against a byte-code array in byte_codes. It is an error to
/* use different address families for the byte_codes and addr_bytes
/* arguments (the first byte-code value contains the expected
/* address family).  The result is non-zero in case of success.
/*
/* ip_match_dump() produces an ASCII dump of a byte-code array.
/* The dump is supposed to be identical to the input pattern
/* modulo upper/lower case or leading nulls with IPv6).  This
/* function is primarily a debugging aid.
/*
/* Arguments
/* .IP addr_bytes
/* Binary network address in network-byte order.
/* .IP byte_codes
/* Byte-code array produced by ip_match_parse().
/* .IP pattern
/* Human-readable address pattern.
/* .IP printable
/* storage for ASCII dump of a byte-code array.
/* IPV4 PATTERN SYNTAX
/* .ad
/* .fi
/* An IPv4 address pattern has four fields separated by ".".
/* Each field is either a decimal number, or a sequence inside
/* "[]" that contains one or more ";"-separated decimal
/* numbers or number..number ranges.
/*
/* Examples of patterns are 1.2.3.4 (matches itself, as one
/* would expect) and 1.2.3.[2,4,6..8] (matches 1.2.3.2, 1.2.3.4,
/* 1.2.3.6, 1.2.3.7, 1.2.3.8).
/*
/* Thus, any pattern field can be a sequence inside "[]", but
/* a "[]" sequence cannot span multiple address fields, and
/* a pattern field cannot contain both a number and a "[]"
/* sequence at the same time.
/*
/* This means that the pattern 1.2.[3.4] is not valid (the
/* sequence [3.4] cannot span two address fields) and the
/* pattern 1.2.3.3[6..9] is also not valid (the last field
/* cannot be both number 3 and sequence [6..9] at the same
/* time).
/*
/* The syntax for IPv4 patterns is as follows:
/*
/* .in +5
/* v4pattern = v4field "." v4field "." v4field "." v4field
/* .br
/* v4field = v4octet | "[" v4sequence "]"
/* .br
/* v4octet = any decimal number in the range 0 through 255
/* .br
/* v4sequence = v4seq_member | v4sequence ";" v4seq_member
/* .br
/* v4seq_member = v4octet | v4octet ".." v4octet
/* .in
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this
/* software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/


/* System library. */

#include <sys_defs.h>
#include <sys/socket.h>
#include <ctype.h>
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <ip_match.h>

 /*
  * Token values. The in-band values are also used as byte-code values.
  */

#define IP_MATCH_CODE_OPEN '[' /* in-band */
#define IP_MATCH_CODE_CLOSE ']' /* in-band */
#define IP_MATCH_CODE_OVAL 'N' /* in-band */
#define IP_MATCH_CODE_RANGE 'R' /* in-band */
#define IP_MATCH_CODE_EOF '\0' /* in-band */
#define IP_MATCH_CODE_ERR 256 /* out-of-band */

 /*
  * SLMs.
  */

#define STR vstring_str
#define LEN VSTRING_LEN

/* ip_match_save - make longer-term copy of byte code */

char   *ip_match_save(const VSTRING *byte_codes)
{
    char   *dst;

    dst = mymalloc(LEN(byte_codes));
    return (memcpy(dst, STR(byte_codes), LEN(byte_codes)));
}

/* ip_match_dump - byte-code pretty printer */

char   *ip_match_dump(VSTRING *printable, const char *byte_codes)
{
    const char *myname = "ip_match_dump";
    const unsigned char *bp;
    int     octet_count = 0;
    int     ch;

    /*
     * Sanity check. Use different dumping loops for AF_INET and AF_INET6.
     */

    if (*byte_codes != AF_INET)
 msg_panic("%s: malformed byte-code header", myname);

    /*
     * Pretty-print and sanity-check the byte codes. Note: the loops in this
     * code have no auto-increment at the end of the iteration. Instead, each
     * byte-code handler bumps the byte-code pointer appropriately.
     */

    VSTRING_RESET(printable);
    bp = (const unsigned char *) byte_codes + 1;
    for (;;) {

 /*
  * Simple numeric field.
 */

 if ((ch = *bp++) == IP_MATCH_CODE_OVAL) {
     vstring_sprintf_append(printable, "%d", *bp);
     bp += 1;
 }

 /*
  * Wild-card numeric field.
 */

 else if (ch == IP_MATCH_CODE_OPEN) {
     vstring_sprintf_append(printable, "[");
     for (;;) {
  /* Numeric range. */
  if ((ch = *bp++) == IP_MATCH_CODE_RANGE) {
      vstring_sprintf_append(printable, "%d..%d", bp[0], bp[1]);
      bp += 2;
  }
  /* Number. */
  else if (ch == IP_MATCH_CODE_OVAL) {
      vstring_sprintf_append(printable, "%d", *bp);
      bp += 1;
  }
  /* End-of-wildcard. */
  else if (ch == IP_MATCH_CODE_CLOSE) {
      break;
  }
  /* Corruption. */
  else {
      msg_panic("%s: unexpected byte code (decimal %d) "
         "after \"%s\"", myname, ch, STR(printable));
  }
  /* Output the wild-card field separator and repeat the loop. */
  if (*bp != IP_MATCH_CODE_CLOSE)
      vstring_sprintf_append(printable, ";");
     }
     vstring_sprintf_append(printable, "]");
 }

 /*
  * Corruption.
 */

 else {
     msg_panic("%s: unexpected byte code (decimal %d) after \"%s\"",
        myname, ch, STR(printable));
 }

 /*
  * Require four octets, not one more, not one less.
 */

 if (++octet_count == 4) {
     if (*bp != 0)
  msg_panic("%s: unexpected byte code (decimal %d) after \"%s\"",
     myname, ch, STR(printable));
     return (STR(printable));
 }
 if (*bp == 0)
     msg_panic("%s: truncated byte code after \"%s\"",
        myname, STR(printable));

 /*
  * Output the address field separator and repeat the loop.
 */

 vstring_sprintf_append(printable, ".");
    }
}

/* ip_match_print_code_prefix - printable byte-code prefix */

static char *ip_match_print_code_prefix(const char *byte_codes, size_t len)
{
    static VSTRING *printable = 0;
    const char *fmt;
    const char *bp;

    /*
     * This is primarily for emergency debugging so we don't care about
     * non-reentrancy.
     */

    if (printable == 0)
 printable = vstring_alloc(100);
    else
 VSTRING_RESET(printable);

    /*
     * Use decimal for IPv4 and hexadecimal otherwise, so that address octet
     * values are easy to recognize.
     */

    fmt = (*byte_codes == AF_INET ? "%d " : "%02x ");
    for (bp = byte_codes; bp < byte_codes + len; bp++)
 vstring_sprintf_append(printable, fmt, *(const unsigned char *) bp);

    return (STR(printable));
}

/* ip_match_execute - byte-code matching engine */

int     ip_match_execute(const char *byte_codes, const char *addr_bytes)
{
    const char *myname = "ip_match_execute";
    const unsigned char *bp;
    const unsigned char *ap;
    int     octet_count = 0;
    int     ch;
    int     matched;

    /*
     * Sanity check. Use different execute loops for AF_INET and AF_INET6.
     */

    if (*byte_codes != AF_INET)
 msg_panic("%s: malformed byte-code header (decimal %d)",
    myname, *(const unsigned char *) byte_codes);

    /*
     * Match the address bytes against the byte codes. Avoid problems with
     * (char -> int) sign extension on architectures with signed characters.
     */

    bp = (const unsigned char *) byte_codes + 1;
    ap = (const unsigned char *) addr_bytes;

    for (octet_count = 0; octet_count < 4; octet_count++, ap++) {

 /*
  * Simple numeric field.
 */

 if ((ch = *bp++) == IP_MATCH_CODE_OVAL) {
     if (*ap == *bp)
  bp += 1;
     else
  return (0);
 }

 /*
  * Wild-card numeric field.
 */

 else if (ch == IP_MATCH_CODE_OPEN) {
     matched = 0;
     for (;;) {
  /* Numeric range. */
  if ((ch = *bp++) == IP_MATCH_CODE_RANGE) {
      if (!matched)
   matched = (*ap >= bp[0] && *ap <= bp[1]);
      bp += 2;
  }
  /* Number. */
  else if (ch == IP_MATCH_CODE_OVAL) {
      if (!matched)
   matched = (*ap == *bp);
      bp += 1;
  }
  /* End-of-wildcard. */
  else if (ch == IP_MATCH_CODE_CLOSE) {
      break;
  }
  /* Corruption. */
  else {
      size_t  len = (const char *) bp - byte_codes - 1;

      msg_panic("%s: unexpected byte code (decimal %d) "
         "after \"%s\"", myname, ch,
         ip_match_print_code_prefix(byte_codes, len));
  }
     }
     if (matched == 0)
  return (0);
 }

 /*
  * Corruption.
 */

 else {
     size_t  len = (const char *) bp - byte_codes - 1;

     msg_panic("%s: unexpected byte code (decimal %d) after \"%s\"",
     myname, ch, ip_match_print_code_prefix(byte_codes, len));
 }
    }
    return (1);
}

/* ip_match_next_token - carve out the next token from input pattern */

static int ip_match_next_token(char **pstart, char **psaved_start, int *poval)
{
    unsigned char *cp;
    int     oval;   /* octet value */
    int     type;   /* token value */

    /*
     * Return a literal, error, or EOF token. Update the read pointer to the
     * start of the next token or leave it at the string terminator.
     */

#define IP_MATCH_RETURN_TOK(next, type) \
    do { *pstart = (char *) (next); return (type); } while (0)

    /*
     * Return a token that contains an IPv4 address octet value.
     */

#define IP_MATCH_RETURN_TOK_VAL(next, type, oval) do { \
 *poval = (oval); IP_MATCH_RETURN_TOK((next), type); \
    } while (0)

    /*
     * Light-weight tokenizer. Each result is an IPv4 address octet value, a
     * literal character value, error, or EOF.
     */

    *psaved_start = *pstart;
    cp = (unsigned char *) *pstart;
    if (ISDIGIT(*cp)) {
 oval = *cp - '0';
 type = IP_MATCH_CODE_OVAL;
 for (cp += 1; ISDIGIT(*cp); cp++) {
     oval *= 10;
     oval += *cp - '0';
     if (oval > 255)
  type = IP_MATCH_CODE_ERR;
 }
 IP_MATCH_RETURN_TOK_VAL(cp, type, oval);
    } else {
 IP_MATCH_RETURN_TOK(*cp ? cp + 1 : cp, *cp);
    }
}

/* ipmatch_print_parse_error - formatted parsing error, with context */

static void PRINTFLIKE(56) ipmatch_print_parse_error(VSTRING *reply,
                     char *start,
                     char *here,
                     char *next,
              const char *fmt,...)
{
    va_list ap;
    int     start_width;
    int     here_width;

    /*
     * Format the error type.
     */

    va_start(ap, fmt);
    vstring_vsprintf(reply, fmt, ap);
    va_end(ap);

    /*
     * Format the error context. The syntax is complex enough that it is
     * worth the effort to precisely indicate what input is in error.
     * 
     * XXX Workaround for %.*s to avoid output when a zero width is specified.
     */

    if (start != 0) {
 start_width = here - start;
 here_width = next - here;
 vstring_sprintf_append(reply, " at \"%.*s>%.*s<%s\"",
          start_width, start_width == 0 ? "" : start,
        here_width, here_width == 0 ? "" : here, next);
    }
}

/* ip_match_parse - parse an entire wild-card address pattern */

char   *ip_match_parse(VSTRING *byte_codes, char *pattern)
{
    int     octet_count;
    char   *saved_cp;
    char   *cp;
    int     token_type;
    int     look_ahead;
    int     oval;
    int     saved_oval;

    /*
     * Simplify this if we change to {} for wildcard notation.
     */

#define FIND_TERMINATOR(start, cp) do { \
 int _level = 0; \
 for (cp = (start) ; *cp; cp++) { \
     if (*cp == '[') _level++; \
     if (*cp != ']'continue; \
     if (--_level == 0break; \
 } \
    } while (0)

    /*
     * Strip [] around the entire pattern.
     */

    if (*pattern == '[') {
 FIND_TERMINATOR(pattern, cp);
 if (cp[0] == 0) {
     vstring_sprintf(byte_codes, "missing \"]\" character");
     return (STR(byte_codes));
 }
 if (cp[1] == 0) {
     *cp = 0;
     pattern += 1;
 }
    }

    /*
     * Sanity check. In this case we can't show any error context.
     */

    if (*pattern == 0) {
 vstring_sprintf(byte_codes, "empty address pattern");
 return (STR(byte_codes));
    }

    /*
     * Simple parser with on-the-fly encoding. For now, IPv4 support only.
     * Use different parser loops for IPv4 and IPv6.
     */

    VSTRING_RESET(byte_codes);
    VSTRING_ADDCH(byte_codes, AF_INET);
    octet_count = 0;
    cp = pattern;

    /*
     * Require four address fields separated by ".", each field containing a
     * numeric octet value or a sequence inside []. The loop head has no test
     * and does not step the loop variable. The tokenizer advances the loop
     * variable, and the loop termination logic is inside the loop.
     */

    for (;;) {
 switch (token_type = ip_match_next_token(&cp, &saved_cp, &oval)) {

     /*
      * Numeric address field.
     */

 case IP_MATCH_CODE_OVAL:
     VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_OVAL);
     VSTRING_ADDCH(byte_codes, oval);
     break;

     /*
      * Wild-card address field.
     */

 case IP_MATCH_CODE_OPEN:
     VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_OPEN);
     /* Require ";"-separated numbers or numeric ranges. */
     for (;;) {
  token_type = ip_match_next_token(&cp, &saved_cp, &oval);
  if (token_type == IP_MATCH_CODE_OVAL) {
      saved_oval = oval;
      look_ahead = ip_match_next_token(&cp, &saved_cp, &oval);
      /* Numeric range. */
      if (look_ahead == '.') {
   /* Brute-force parsing. */
   if (ip_match_next_token(&cp, &saved_cp, &oval) == '.'
       && ip_match_next_token(&cp, &saved_cp, &oval)
       == IP_MATCH_CODE_OVAL
       && saved_oval <= oval) {
       VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_RANGE);
       VSTRING_ADDCH(byte_codes, saved_oval);
       VSTRING_ADDCH(byte_codes, oval);
       look_ahead =
    ip_match_next_token(&cp, &saved_cp, &oval);
   } else {
       ipmatch_print_parse_error(byte_codes, pattern,
            saved_cp, cp,
            "numeric range error");
       return (STR(byte_codes));
   }
      }
      /* Single number. */
      else {
   VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_OVAL);
   VSTRING_ADDCH(byte_codes, saved_oval);
      }
      /* Require ";" or end-of-wildcard. */
      token_type = look_ahead;
      if (token_type == ';') {
   continue;
      } else if (token_type == IP_MATCH_CODE_CLOSE) {
   break;
      } else {
   ipmatch_print_parse_error(byte_codes, pattern,
        saved_cp, cp,
        "need \";\" or \"%c\"",
        IP_MATCH_CODE_CLOSE);
   return (STR(byte_codes));
      }
  } else {
      ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
           "need decimal number 0..255");
      return (STR(byte_codes));
  }
     }
     VSTRING_ADDCH(byte_codes, IP_MATCH_CODE_CLOSE);
     break;

     /*
      * Invalid field.
     */

 default:
     ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
          "need decimal number 0..255 or \"%c\"",
          IP_MATCH_CODE_OPEN);
     return (STR(byte_codes));
 }
 octet_count += 1;

 /*
  * Require four address fields. Not one more, not one less.
 */

 if (octet_count == 4) {
     if (*cp != 0) {
  (void) ip_match_next_token(&cp, &saved_cp, &oval);
  ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
       "garbage after pattern");
  return (STR(byte_codes));
     }
     VSTRING_ADDCH(byte_codes, 0);
     return (0);
 }

 /*
  * Require "." before the next address field.
 */

 if (ip_match_next_token(&cp, &saved_cp, &oval) != '.') {
     ipmatch_print_parse_error(byte_codes, pattern, saved_cp, cp,
          "need \".\"");
     return (STR(byte_codes));
 }
    }
}

#ifdef TEST

 /*
  * Dummy main program for regression tests.
  */

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>

int     main(int argc, char **argv)
{
    VSTRING *byte_codes = vstring_alloc(100);
    VSTRING *line_buf = vstring_alloc(100);
    char   *bufp;
    char   *err;
    char   *user_pattern;
    char   *user_address;
    int     echo_input = !isatty(0);

    /*
     * Iterate over the input stream. The input format is a pattern, followed
     * by optional addresses to match against.
     */

    while (vstring_fgets_nonl(line_buf, VSTREAM_IN)) {
 bufp = STR(line_buf);
 if (echo_input) {
     vstream_printf("> %s\n", bufp);
     vstream_fflush(VSTREAM_OUT);
 }
 if (*bufp == '#')
     continue;
 if ((user_pattern = mystrtok(&bufp, " \t")) == 0)
     continue;

 /*
  * Parse and dump the pattern.
 */

 if ((err = ip_match_parse(byte_codes, user_pattern)) != 0) {
     vstream_printf("Error: %s\n", err);
 } else {
     vstream_printf("Code: %s\n",
      ip_match_dump(line_buf, STR(byte_codes)));
 }
 vstream_fflush(VSTREAM_OUT);

 /*
  * Match the optional patterns.
 */

 while ((user_address = mystrtok(&bufp, " \t")) != 0) {
     struct in_addr netw_addr;

     switch (inet_pton(AF_INET, user_address, &netw_addr)) {
     case 1:
  vstream_printf("Match %s: %s\n", user_address,
          ip_match_execute(STR(byte_codes),
      (char *) &netw_addr.s_addr) ?
          "yes" : "no");
  break;
     case 0:
  vstream_printf("bad address syntax: %s\n", user_address);
  break;
     case -1:
  vstream_printf("%s: %m\n", user_address);
  break;
     }
     vstream_fflush(VSTREAM_OUT);
 }
    }
    vstring_free(line_buf);
    vstring_free(byte_codes);
    exit(0);
}

#endif

Messung V0.5 in Prozent
C=75 H=96 G=86

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-09) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.