]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.8-20101126
authorWietse Venema <wietse@porcupine.org>
Fri, 26 Nov 2010 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:36:42 +0000 (06:36 +0000)
30 files changed:
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/html/postconf.5.html
postfix/man/man5/postconf.5
postfix/proto/postconf.proto
postfix/src/global/mail_version.h
postfix/src/postscreen/Makefile.in
postfix/src/postscreen/postscreen_dnsbl.c
postfix/src/postscreen/postscreen_smtpd.c
postfix/src/postscreen/postscreen_tests.c
postfix/src/qmqpd/Makefile.in
postfix/src/qmqpd/qmqpd_peer.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_peer.c
postfix/src/util/Makefile.in
postfix/src/util/ip_lmatch.c [new file with mode: 0644]
postfix/src/util/ip_lmatch.h [new file with mode: 0644]
postfix/src/util/ip_lmatch.in [new file with mode: 0644]
postfix/src/util/ip_lmatch.ref [new file with mode: 0644]
postfix/src/util/ip_match.c [new file with mode: 0644]
postfix/src/util/ip_match.h [new file with mode: 0644]
postfix/src/util/ip_match.in [new file with mode: 0644]
postfix/src/util/ip_match.ref [new file with mode: 0644]
postfix/src/util/stream_recv_fd.c
postfix/src/util/stream_send_fd.c
postfix/src/util/unix_recv_fd.c
postfix/src/util/unix_send_fd.c

index a7454217f91bd27890378cf6fe72c03668362627..960ad72bd84b8b3def62638942cb6ddea1fab569 100644 (file)
@@ -15826,13 +15826,13 @@ Apologies for any names omitted.
        Bugfix (introduced Postfix 2.2): Postfix no longer appends
        the system default CA certificates to the lists specified
        with *_tls_CAfile or with *_tls_CApath.  This prevents
-       third-party certificates from being trusted and given mail
-       relay permission with permit_tls_all_clientcerts.  This
-       change may break valid configurations that do not use
-       permit_tls_all_clientcerts.  To get the old behavior, specify
-       "tls_append_default_CA = yes".  Files: tls/tls_certkey.c,
-       tls/tls_misc.c, global/mail_params.h.  proto/postconf.proto,
-       mantools/postlink.
+       third-party certificates from getting mail relay permission
+       with the permit_tls_all_clientcerts feature.  Unfortunately
+       this may cause compatibility problems with configurations
+       that rely on certificate verification for other purposes.
+       To get the old behavior, specify "tls_append_default_CA =
+       yes".  Files: tls/tls_certkey.c, tls/tls_misc.c,
+       global/mail_params.h.  proto/postconf.proto, mantools/postlink.
 
 20100615
 
@@ -16136,3 +16136,43 @@ Apologies for any names omitted.
        DEFER_IF_REJECT when DNSWL lookup fails.  The implementation
        is based on a design documented by Noel Jones (August 2010).
        File: smtpd/smtpd_check.c.
+
+20101108
+
+       Workaround: strip off IPv6 datalink suffix from peer address
+       to avoid problems with strict address checking code. Files:
+       smtpd/smtpd_peer.c, qmqpd/qmqpd_peer.c.
+
+20101114
+
+       Robustness: postscreen(8) now implements a time limit on
+       reading an entire command, instead of a time limit for
+       reading individual characters. File: postscreen/postscreen_smtpd.c.
+
+20101117
+
+       Bugfix: the "421" reply after Milter error was overruled
+       by Postfix 1.1 code that replied with "503" for RFC 2821
+       compliance. We now make an exception for "final" replies,
+       as permitted by RFC. Solution by Victor Duchovni. File:
+       smtpd/smtpd.c.
+
+20101023
+
+       Cleanup: don't apply reject_rhsbl_helo to non-domain forms
+       such as network addresses.  This would cause false positives
+       with dbl.spamhaus.org.  File: smtpd/smtpd_check.c.
+
+20101124-6
+
+       Feature: pattern matching for DNSWL/DNSBL responses.  For
+       example, with "reject_rbl_client example.com=d.d.d.d", each
+       "d" can now be a pattern inside "[]" that contains one or
+       more comma-separated decimal numbers or number..number
+       ranges.  Files: smtpd/smtpd_check.c, postscreen/postscreen_dnsbl.c,
+       util/ip_match.c, util/ip_match.h.
+
+20101126
+
+       Cleanup: don't log "blocked using example.com=127.0.0.1",
+       just log the domain name. File: smtpd/smtpd_check.c.
index 3f1a2cdb5617cbbd7d7681dd41d03e83d8b792b0..c226272603994730643deb88367e298d4a8d8ebf 100644 (file)
@@ -33,6 +33,40 @@ This is supported only when the default value is stress-dependent
 postscreen parameters always evaluate as if the stress value is 
 equal to the empty string.
 
+Major changes with snapshot 20101126
+====================================
+
+Support for address patterns in DNSBL and DNSWL lookup results. 
+
+For example, "reject_rbl_client example.com = 127.0.0.[2,4,6..8]"
+will reject clients when the lookup result is 127.0.0.2, 127.0.0.4,
+127.0.0.6, 127.0.0.7, or 127.0.0.8.
+
+The setting "postscreen_dnsbl_sites = example.com=127.0.0.[2,4,6..8]"
+rejects the same clients.
+
+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 comma-separated decimal numbers or number..number
+ranges.
+
+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:
+
+v4pattern = v4field "." v4field "." v4field "." v4field
+v4field = v4octet | "[" v4sequence "]"
+v4octet = any decimal number in the range 0 through 255
+v4sequence = v4seq_member | v4sequence "," v4seq_member
+v4seq_member = v4octet | v4octet ".." v4octet
+
 Major changes with snapshot 20101105
 ====================================
 
@@ -210,15 +244,15 @@ that sends mail into the after-filter SMTP server.
 
 Incompatibility with snapshot 20100610
 ======================================
-
 Postfix no longer appends the system-supplied default CA certificates
 to the lists specified with *_tls_CAfile or with *_tls_CApath. This
-prevents third-party certificates from being trusted and given mail
-relay permission with permit_tls_all_clientcerts.
-
-Unfortunately this change may break certificate verification on
-sites that don't use permit_tls_all_clientcerts.  Specify
-"tls_append_default_CA = yes" for backwards compatibility.
+prevents third-party certificates from getting mail relay permission
+with the permit_tls_all_clientcerts feature.
+Unfortunately this change may cause compatibility problems when
+configurations rely on certificate verification for other purposes.
+Specify "tls_append_default_CA = yes" for backwards compatibility.
 
 Incompatibility with snapshot 20100101
 ======================================
index 90490da14f1fcdf5b9261a62d974d55c7b2d3474..a57d28a5960c0a31cd4e578c10a4d90940967a62 100644 (file)
@@ -4,6 +4,17 @@ Wish list:
 
        anvil rate limit for sasl_username.
 
+       postscreen per-client connection count limit.
+
+       smtpd xclient option for sasl_username.
+
+       Documentation: add a note that smtpd_helo_required=yes is
+       needed to really enforce HELO restrictions.
+
+       Use different ipc_timeout settings for email message
+       transactions (smtpd, pickup)->cleanup and for quick query/reply
+       transactions such as address rewriting/resolution.
+
        permit_tempfail_action (default: defer_if_reject) to be
        used as the default value for dnswl_tempfail_action and
        rhswl_tempfail_action. Steal liberally from the code that
index 66afd8452c98b3e6ba6a17ca02cf0089ca7ca872..c9d86854b26c830ea1dbf65fbb65d3708f19d62a 100644 (file)
@@ -6802,7 +6802,8 @@ and error commands.  </p>
 (default: ${stress?10}${stress:300}s)</b></DT><DD>
 
 <p> The command "read" time limit for <a href="postscreen.8.html">postscreen(8)</a>'s built-in SMTP
-protocol engine.  </p>
+protocol engine. This bounds the time to receive an entire command.
+</p>
 
 <p> This feature is available in Postfix 2.8.  </p>
 
@@ -11679,9 +11680,12 @@ Postfix version 2.5).  This feature is available with Postfix version
 
 <dd>Reject the request when the reversed client network address is
 listed with the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i>
-(Postfix version 2.1 and later only).  If no "<i>=d.d.d.d</i>" is
-specified, reject the request when the reversed client network
-address is listed with any A record under <i>rbl_domain</i>. <br>
+(Postfix version 2.1 and later only).  Each "<i>d</i>" can be a
+pattern inside "[]" that contains one or more comma-separated decimal
+numbers or number..number ranges (Postfix version 2.8 and later).
+If no "<i>=d.d.d.d</i>" is specified, reject the request when the
+reversed client network address is listed with any A record under
+<i>rbl_domain</i>. <br>
 The <a href="postconf.5.html#maps_rbl_reject_code">maps_rbl_reject_code</a> parameter specifies the response code for
 rejected requests (default:  554), the <a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a>  parameter
 specifies the default server reply, and the <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a>  parameter
@@ -11692,6 +11696,8 @@ This feature is available in Postfix 2.0 and later.  </dd>
 
 <dd>Accept the request when the reversed client network address is
 listed with the A record "<i>d.d.d.d</i>" under <i>dnswl_domain</i>.
+Each "<i>d</i>" can be a pattern inside "[]" that contains one or
+more comma-separated decimal numbers or number..number ranges.
 If no "<i>=d.d.d.d</i>" is specified, accept the request when the
 reversed client network address is listed with any A record under
 <i>dnswl_domain</i>. <br> For safety, <a href="postconf.5.html#permit_dnswl_client">permit_dnswl_client</a> is silently
@@ -11699,12 +11705,15 @@ ignored when it would override <a href="postconf.5.html#reject_unauth_destinatio
 result is DEFER_IF_REJECT when whitelist lookup fails.  This feature
 is available in Postfix 2.8 and later.  </dd>
 
-</dd> <dt><b><a name="reject_rhsbl_client">reject_rhsbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
+<dt><b><a name="reject_rhsbl_client">reject_rhsbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
 
 <dd>Reject the request when the client hostname is listed with the
 A record "<i>d.d.d.d</i>" under <i>rbl_domain</i> (Postfix version
-2.1 and later only).  If no "<i>=d.d.d.d</i>" is specified, reject
-the request when the client hostname is listed with
+2.1 and later only).  Each "<i>d</i>" can be a pattern inside "[]"
+that contains one or more comma-separated decimal numbers or
+number..number ranges (Postfix version 2.8 and later).  If no
+"<i>=d.d.d.d</i>" is specified, reject the request when the client
+hostname is listed with
 any A record under <i>rbl_domain</i>. See the <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a>
 description above for additional RBL related configuration parameters.
 This feature is available in Postfix 2.0 and later; with Postfix
@@ -11714,7 +11723,9 @@ produce better results.  </dd>
 </dd> <dt><b><a name="permit_rhswl_client">permit_rhswl_client <i>rhswl_domain=d.d.d.d</i></a></b></dt>
 
 <dd>Accept the request when the client hostname is listed with the
-A record "<i>d.d.d.d</i>" under <i>rhswl_domain</i>.  If no
+A record "<i>d.d.d.d</i>" under <i>rhswl_domain</i>.  Each "<i>d</i>"
+can be a pattern inside "[]" that contains one or more comma-separated
+decimal numbers or number..number ranges. If no
 "<i>=d.d.d.d</i>" is specified, accept the request when the client
 hostname is listed with any A record under <i>rhswl_domain</i>.
 <br> Caution: client name whitelisting is fragile, since the client
@@ -11730,6 +11741,8 @@ when whitelist lookup fails.  This feature is available in Postfix
 
 <dd>Reject the request when the unverified reverse client hostname
 is listed with the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i>.
+Each "<i>d</i>" can be a pattern inside "[]" that contains one or
+more comma-separated decimal numbers or number..number ranges.
 If no "<i>=d.d.d.d</i>" is specified, reject the request when the
 unverified reverse client hostname is listed with any A record under
 <i>rbl_domain</i>. See the <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> description above for
@@ -12368,7 +12381,10 @@ rejected requests (default: 504).</dd>
 
 <dd>Reject the request when the HELO or EHLO hostname hostname is
 listed with the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i>
-(Postfix version 2.1 and later only).  If no "<i>=d.d.d.d</i>" is
+(Postfix version 2.1 and later only).  Each "<i>d</i>" can be a
+pattern inside "[]" that contains one or more comma-separated decimal
+numbers or number..number ranges (Postfix version 2.8 and later).
+If no "<i>=d.d.d.d</i>" is
 specified, reject the request when the HELO or EHLO hostname is
 listed with any A record under <i>rbl_domain</i>. See the
 <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> description for additional RBL related configuration
@@ -12784,7 +12800,10 @@ rejected requests (default: 504). </dd>
 
 <dd>Reject the request when the RCPT TO domain is listed with the
 A record "<i>d.d.d.d</i>" under <i>rbl_domain</i> (Postfix version
-2.1 and later only).  If no "<i>=d.d.d.d</i>" is specified, reject
+2.1 and later only).  Each "<i>d</i>" can be a pattern inside "[]"
+that contains one or more comma-separated decimal numbers or
+number..number ranges (Postfix version 2.8 and later). If no
+"<i>=d.d.d.d</i>" is specified, reject
 the request when the RCPT TO domain is listed with
 any A record under <i>rbl_domain</i>. <br> The <a href="postconf.5.html#maps_rbl_reject_code">maps_rbl_reject_code</a>
 parameter specifies the response code for rejected requests (default:
@@ -13328,7 +13347,10 @@ rejected requests (default: 504). </dd>
 
 <dd>Reject the request when the MAIL FROM domain is listed with
 the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i> (Postfix
-version 2.1 and later only).  If no "<i>=d.d.d.d</i>" is specified,
+version 2.1 and later only).  Each "<i>d</i>" can be a pattern
+inside "[]" that contains one or more comma-separated decimal numbers
+or number..number ranges (Postfix version 2.8 and later). If no
+"<i>=d.d.d.d</i>" is specified,
 reject the request when the MAIL FROM domain is
 listed with any A record under <i>rbl_domain</i>. <br> The
 <a href="postconf.5.html#maps_rbl_reject_code">maps_rbl_reject_code</a> parameter specifies the response code for
index db80efbe150635f917ecf424f172774e6e837cb4..3a98c78b89cfcb630fba1f89bd86f0e34f64701a 100644 (file)
@@ -3828,7 +3828,7 @@ and error commands.
 This feature is available in Postfix 2.8.
 .SH postscreen_command_time_limit (default: ${stress?10}${stress:300}s)
 The command "read" time limit for \fBpostscreen\fR(8)'s built-in SMTP
-protocol engine.
+protocol engine. This bounds the time to receive an entire command.
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_disable_vrfy_command (default: $disable_vrfy_command)
@@ -7166,9 +7166,12 @@ Postfix version 2.5).  This feature is available with Postfix version
 .IP "\fBreject_rbl_client \fIrbl_domain=d.d.d.d\fR\fR"
 Reject the request when the reversed client network address is
 listed with the A record "\fId.d.d.d\fR" under \fIrbl_domain\fR
-(Postfix version 2.1 and later only).  If no "\fI=d.d.d.d\fR" is
-specified, reject the request when the reversed client network
-address is listed with any A record under \fIrbl_domain\fR.
+(Postfix version 2.1 and later only).  Each "\fId\fR" can be a
+pattern inside "[]" that contains one or more comma-separated decimal
+numbers or number..number ranges (Postfix version 2.8 and later).
+If no "\fI=d.d.d.d\fR" is specified, reject the request when the
+reversed client network address is listed with any A record under
+\fIrbl_domain\fR.
 .br
 The maps_rbl_reject_code parameter specifies the response code for
 rejected requests (default:  554), the default_rbl_reply  parameter
@@ -7178,6 +7181,8 @@ This feature is available in Postfix 2.0 and later.
 .IP "\fBpermit_dnswl_client \fIdnswl_domain=d.d.d.d\fR\fR"
 Accept the request when the reversed client network address is
 listed with the A record "\fId.d.d.d\fR" under \fIdnswl_domain\fR.
+Each "\fId\fR" can be a pattern inside "[]" that contains one or
+more comma-separated decimal numbers or number..number ranges.
 If no "\fI=d.d.d.d\fR" is specified, accept the request when the
 reversed client network address is listed with any A record under
 \fIdnswl_domain\fR.
@@ -7189,8 +7194,11 @@ is available in Postfix 2.8 and later.
 .IP "\fBreject_rhsbl_client \fIrbl_domain=d.d.d.d\fR\fR"
 Reject the request when the client hostname is listed with the
 A record "\fId.d.d.d\fR" under \fIrbl_domain\fR (Postfix version
-2.1 and later only).  If no "\fI=d.d.d.d\fR" is specified, reject
-the request when the client hostname is listed with
+2.1 and later only).  Each "\fId\fR" can be a pattern inside "[]"
+that contains one or more comma-separated decimal numbers or
+number..number ranges (Postfix version 2.8 and later).  If no
+"\fI=d.d.d.d\fR" is specified, reject the request when the client
+hostname is listed with
 any A record under \fIrbl_domain\fR. See the reject_rbl_client
 description above for additional RBL related configuration parameters.
 This feature is available in Postfix 2.0 and later; with Postfix
@@ -7198,7 +7206,9 @@ version 2.8 and later, reject_rhsbl_reverse_client will usually
 produce better results.
 .IP "\fBpermit_rhswl_client \fIrhswl_domain=d.d.d.d\fR\fR"
 Accept the request when the client hostname is listed with the
-A record "\fId.d.d.d\fR" under \fIrhswl_domain\fR.  If no
+A record "\fId.d.d.d\fR" under \fIrhswl_domain\fR.  Each "\fId\fR"
+can be a pattern inside "[]" that contains one or more comma-separated
+decimal numbers or number..number ranges. If no
 "\fI=d.d.d.d\fR" is specified, accept the request when the client
 hostname is listed with any A record under \fIrhswl_domain\fR.
 .br
@@ -7214,6 +7224,8 @@ when whitelist lookup fails.  This feature is available in Postfix
 .IP "\fBreject_rhsbl_reverse_client \fIrbl_domain=d.d.d.d\fR\fR"
 Reject the request when the unverified reverse client hostname
 is listed with the A record "\fId.d.d.d\fR" under \fIrbl_domain\fR.
+Each "\fId\fR" can be a pattern inside "[]" that contains one or
+more comma-separated decimal numbers or number..number ranges.
 If no "\fI=d.d.d.d\fR" is specified, reject the request when the
 unverified reverse client hostname is listed with any A record under
 \fIrbl_domain\fR. See the reject_rbl_client description above for
@@ -7682,7 +7694,10 @@ rejected requests (default: 504).
 .IP "\fBreject_rhsbl_helo \fIrbl_domain=d.d.d.d\fR\fR"
 Reject the request when the HELO or EHLO hostname hostname is
 listed with the A record "\fId.d.d.d\fR" under \fIrbl_domain\fR
-(Postfix version 2.1 and later only).  If no "\fI=d.d.d.d\fR" is
+(Postfix version 2.1 and later only).  Each "\fId\fR" can be a
+pattern inside "[]" that contains one or more comma-separated decimal
+numbers or number..number ranges (Postfix version 2.8 and later).
+If no "\fI=d.d.d.d\fR" is
 specified, reject the request when the HELO or EHLO hostname is
 listed with any A record under \fIrbl_domain\fR. See the
 reject_rbl_client description for additional RBL related configuration
@@ -7924,7 +7939,10 @@ rejected requests (default: 504).
 .IP "\fBreject_rhsbl_recipient \fIrbl_domain=d.d.d.d\fR\fR"
 Reject the request when the RCPT TO domain is listed with the
 A record "\fId.d.d.d\fR" under \fIrbl_domain\fR (Postfix version
-2.1 and later only).  If no "\fI=d.d.d.d\fR" is specified, reject
+2.1 and later only).  Each "\fId\fR" can be a pattern inside "[]"
+that contains one or more comma-separated decimal numbers or
+number..number ranges (Postfix version 2.8 and later). If no
+"\fI=d.d.d.d\fR" is specified, reject
 the request when the RCPT TO domain is listed with
 any A record under \fIrbl_domain\fR.
 .br
@@ -8294,7 +8312,10 @@ rejected requests (default: 504).
 .IP "\fBreject_rhsbl_sender \fIrbl_domain=d.d.d.d\fR\fR"
 Reject the request when the MAIL FROM domain is listed with
 the A record "\fId.d.d.d\fR" under \fIrbl_domain\fR (Postfix
-version 2.1 and later only).  If no "\fI=d.d.d.d\fR" is specified,
+version 2.1 and later only).  Each "\fId\fR" can be a pattern
+inside "[]" that contains one or more comma-separated decimal numbers
+or number..number ranges (Postfix version 2.8 and later). If no
+"\fI=d.d.d.d\fR" is specified,
 reject the request when the MAIL FROM domain is
 listed with any A record under \fIrbl_domain\fR.
 .br
index 6e3ce4e1e4542fdadae1e7c9e33d5a371a510649..16d9d3942b1f5ccabd2aa26a0b431545fe178be5 100644 (file)
@@ -4879,9 +4879,12 @@ Postfix version 2.5).  This feature is available with Postfix version
 
 <dd>Reject the request when the reversed client network address is
 listed with the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i>
-(Postfix version 2.1 and later only).  If no "<i>=d.d.d.d</i>" is
-specified, reject the request when the reversed client network
-address is listed with any A record under <i>rbl_domain</i>. <br>
+(Postfix version 2.1 and later only).  Each "<i>d</i>" can be a
+pattern inside "[]" that contains one or more comma-separated decimal
+numbers or number..number ranges (Postfix version 2.8 and later).
+If no "<i>=d.d.d.d</i>" is specified, reject the request when the
+reversed client network address is listed with any A record under
+<i>rbl_domain</i>. <br>
 The maps_rbl_reject_code parameter specifies the response code for
 rejected requests (default:  554), the default_rbl_reply  parameter
 specifies the default server reply, and the rbl_reply_maps  parameter
@@ -4892,6 +4895,8 @@ This feature is available in Postfix 2.0 and later.  </dd>
 
 <dd>Accept the request when the reversed client network address is
 listed with the A record "<i>d.d.d.d</i>" under <i>dnswl_domain</i>.
+Each "<i>d</i>" can be a pattern inside "[]" that contains one or
+more comma-separated decimal numbers or number..number ranges.
 If no "<i>=d.d.d.d</i>" is specified, accept the request when the
 reversed client network address is listed with any A record under
 <i>dnswl_domain</i>. <br> For safety, permit_dnswl_client is silently
@@ -4903,8 +4908,11 @@ is available in Postfix 2.8 and later.  </dd>
 
 <dd>Reject the request when the client hostname is listed with the
 A record "<i>d.d.d.d</i>" under <i>rbl_domain</i> (Postfix version
-2.1 and later only).  If no "<i>=d.d.d.d</i>" is specified, reject
-the request when the client hostname is listed with
+2.1 and later only).  Each "<i>d</i>" can be a pattern inside "[]"
+that contains one or more comma-separated decimal numbers or
+number..number ranges (Postfix version 2.8 and later).  If no
+"<i>=d.d.d.d</i>" is specified, reject the request when the client
+hostname is listed with
 any A record under <i>rbl_domain</i>. See the reject_rbl_client
 description above for additional RBL related configuration parameters.
 This feature is available in Postfix 2.0 and later; with Postfix
@@ -4914,7 +4922,9 @@ produce better results.  </dd>
 </dd> <dt><b><a name="permit_rhswl_client">permit_rhswl_client <i>rhswl_domain=d.d.d.d</i></a></b></dt>
 
 <dd>Accept the request when the client hostname is listed with the
-A record "<i>d.d.d.d</i>" under <i>rhswl_domain</i>.  If no
+A record "<i>d.d.d.d</i>" under <i>rhswl_domain</i>.  Each "<i>d</i>"
+can be a pattern inside "[]" that contains one or more comma-separated
+decimal numbers or number..number ranges. If no
 "<i>=d.d.d.d</i>" is specified, accept the request when the client
 hostname is listed with any A record under <i>rhswl_domain</i>.
 <br> Caution: client name whitelisting is fragile, since the client
@@ -4930,6 +4940,8 @@ when whitelist lookup fails.  This feature is available in Postfix
 
 <dd>Reject the request when the unverified reverse client hostname
 is listed with the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i>.
+Each "<i>d</i>" can be a pattern inside "[]" that contains one or
+more comma-separated decimal numbers or number..number ranges.
 If no "<i>=d.d.d.d</i>" is specified, reject the request when the
 unverified reverse client hostname is listed with any A record under
 <i>rbl_domain</i>. See the reject_rbl_client description above for
@@ -5369,7 +5381,10 @@ rejected requests (default: 504).</dd>
 
 <dd>Reject the request when the HELO or EHLO hostname hostname is
 listed with the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i>
-(Postfix version 2.1 and later only).  If no "<i>=d.d.d.d</i>" is
+(Postfix version 2.1 and later only).  Each "<i>d</i>" can be a
+pattern inside "[]" that contains one or more comma-separated decimal
+numbers or number..number ranges (Postfix version 2.8 and later).
+If no "<i>=d.d.d.d</i>" is
 specified, reject the request when the HELO or EHLO hostname is
 listed with any A record under <i>rbl_domain</i>. See the
 reject_rbl_client description for additional RBL related configuration
@@ -5659,7 +5674,10 @@ rejected requests (default: 504). </dd>
 
 <dd>Reject the request when the RCPT TO domain is listed with the
 A record "<i>d.d.d.d</i>" under <i>rbl_domain</i> (Postfix version
-2.1 and later only).  If no "<i>=d.d.d.d</i>" is specified, reject
+2.1 and later only).  Each "<i>d</i>" can be a pattern inside "[]"
+that contains one or more comma-separated decimal numbers or
+number..number ranges (Postfix version 2.8 and later). If no
+"<i>=d.d.d.d</i>" is specified, reject
 the request when the RCPT TO domain is listed with
 any A record under <i>rbl_domain</i>. <br> The maps_rbl_reject_code
 parameter specifies the response code for rejected requests (default:
@@ -6034,7 +6052,10 @@ rejected requests (default: 504). </dd>
 
 <dd>Reject the request when the MAIL FROM domain is listed with
 the A record "<i>d.d.d.d</i>" under <i>rbl_domain</i> (Postfix
-version 2.1 and later only).  If no "<i>=d.d.d.d</i>" is specified,
+version 2.1 and later only).  Each "<i>d</i>" can be a pattern
+inside "[]" that contains one or more comma-separated decimal numbers
+or number..number ranges (Postfix version 2.8 and later). If no
+"<i>=d.d.d.d</i>" is specified,
 reject the request when the MAIL FROM domain is
 listed with any A record under <i>rbl_domain</i>. <br> The
 maps_rbl_reject_code parameter specifies the response code for
@@ -13062,7 +13083,8 @@ and error commands.  </p>
 %PARAM postscreen_command_time_limit ${stress?10}${stress:300}s
 
 <p> The command "read" time limit for postscreen(8)'s built-in SMTP
-protocol engine.  </p>
+protocol engine. This bounds the time to receive an entire command.
+</p>
 
 <p> This feature is available in Postfix 2.8.  </p>
 
index d7e5eb58029cdab3f8af4f904e76b33558a0819d..a5e6a34bb6141fd2f581777ec656196271ee0d16 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20101108"
+#define MAIL_RELEASE_DATE      "20101126"
 #define MAIL_VERSION_NUMBER    "2.8"
 
 #ifdef SNAPSHOT
index 3b35045d270b8b464edca7537dec2f4d33f16ced..6957030b2c09bab24090e37e7efd8ac778456e65 100644 (file)
@@ -81,6 +81,7 @@ postscreen.o: ../../include/myaddrinfo.h
 postscreen.o: ../../include/mymalloc.h
 postscreen.o: ../../include/name_code.h
 postscreen.o: ../../include/set_eugid.h
+postscreen.o: ../../include/string_list.h
 postscreen.o: ../../include/sys_defs.h
 postscreen.o: ../../include/vbuf.h
 postscreen.o: ../../include/vstream.h
@@ -95,6 +96,7 @@ postscreen_dict.o: ../../include/events.h
 postscreen_dict.o: ../../include/match_list.h
 postscreen_dict.o: ../../include/match_ops.h
 postscreen_dict.o: ../../include/msg.h
+postscreen_dict.o: ../../include/string_list.h
 postscreen_dict.o: ../../include/sys_defs.h
 postscreen_dict.o: ../../include/vbuf.h
 postscreen_dict.o: ../../include/vstream.h
@@ -110,6 +112,7 @@ postscreen_dnsbl.o: ../../include/dict_cache.h
 postscreen_dnsbl.o: ../../include/events.h
 postscreen_dnsbl.o: ../../include/htable.h
 postscreen_dnsbl.o: ../../include/iostuff.h
+postscreen_dnsbl.o: ../../include/ip_match.h
 postscreen_dnsbl.o: ../../include/mail_params.h
 postscreen_dnsbl.o: ../../include/mail_proto.h
 postscreen_dnsbl.o: ../../include/match_list.h
@@ -117,6 +120,7 @@ postscreen_dnsbl.o: ../../include/match_ops.h
 postscreen_dnsbl.o: ../../include/msg.h
 postscreen_dnsbl.o: ../../include/mymalloc.h
 postscreen_dnsbl.o: ../../include/split_at.h
+postscreen_dnsbl.o: ../../include/string_list.h
 postscreen_dnsbl.o: ../../include/sys_defs.h
 postscreen_dnsbl.o: ../../include/valid_hostname.h
 postscreen_dnsbl.o: ../../include/vbuf.h
@@ -134,6 +138,7 @@ postscreen_early.o: ../../include/match_list.h
 postscreen_early.o: ../../include/match_ops.h
 postscreen_early.o: ../../include/msg.h
 postscreen_early.o: ../../include/mymalloc.h
+postscreen_early.o: ../../include/string_list.h
 postscreen_early.o: ../../include/stringops.h
 postscreen_early.o: ../../include/sys_defs.h
 postscreen_early.o: ../../include/vbuf.h
@@ -152,6 +157,7 @@ postscreen_misc.o: ../../include/mail_params.h
 postscreen_misc.o: ../../include/match_list.h
 postscreen_misc.o: ../../include/match_ops.h
 postscreen_misc.o: ../../include/msg.h
+postscreen_misc.o: ../../include/string_list.h
 postscreen_misc.o: ../../include/sys_defs.h
 postscreen_misc.o: ../../include/vbuf.h
 postscreen_misc.o: ../../include/vstream.h
@@ -168,6 +174,7 @@ postscreen_send.o: ../../include/iostuff.h
 postscreen_send.o: ../../include/match_list.h
 postscreen_send.o: ../../include/match_ops.h
 postscreen_send.o: ../../include/msg.h
+postscreen_send.o: ../../include/string_list.h
 postscreen_send.o: ../../include/sys_defs.h
 postscreen_send.o: ../../include/vbuf.h
 postscreen_send.o: ../../include/vstream.h
@@ -181,12 +188,14 @@ postscreen_smtpd.o: ../../include/dict.h
 postscreen_smtpd.o: ../../include/dict_cache.h
 postscreen_smtpd.o: ../../include/events.h
 postscreen_smtpd.o: ../../include/iostuff.h
+postscreen_smtpd.o: ../../include/is_header.h
 postscreen_smtpd.o: ../../include/mail_params.h
 postscreen_smtpd.o: ../../include/mail_proto.h
 postscreen_smtpd.o: ../../include/match_list.h
 postscreen_smtpd.o: ../../include/match_ops.h
 postscreen_smtpd.o: ../../include/msg.h
 postscreen_smtpd.o: ../../include/mymalloc.h
+postscreen_smtpd.o: ../../include/string_list.h
 postscreen_smtpd.o: ../../include/stringops.h
 postscreen_smtpd.o: ../../include/sys_defs.h
 postscreen_smtpd.o: ../../include/vbuf.h
@@ -208,6 +217,7 @@ postscreen_state.o: ../../include/match_ops.h
 postscreen_state.o: ../../include/msg.h
 postscreen_state.o: ../../include/mymalloc.h
 postscreen_state.o: ../../include/name_mask.h
+postscreen_state.o: ../../include/string_list.h
 postscreen_state.o: ../../include/sys_defs.h
 postscreen_state.o: ../../include/vbuf.h
 postscreen_state.o: ../../include/vstream.h
@@ -223,6 +233,7 @@ postscreen_tests.o: ../../include/mail_params.h
 postscreen_tests.o: ../../include/match_list.h
 postscreen_tests.o: ../../include/match_ops.h
 postscreen_tests.o: ../../include/msg.h
+postscreen_tests.o: ../../include/string_list.h
 postscreen_tests.o: ../../include/sys_defs.h
 postscreen_tests.o: ../../include/vbuf.h
 postscreen_tests.o: ../../include/vstream.h
index 289a38bf121e504d16a39cb79f790e2df6e95c25..d28c90e426122d26cdce478519fba29f0d0256b1 100644 (file)
@@ -54,6 +54,9 @@
 /* System library. */
 
 #include <sys_defs.h>
+#include <sys/socket.h>                        /* AF_INET */
+#include <netinet/in.h>                        /* inet_pton() */
+#include <arpa/inet.h>                 /* inet_pton() */
 #include <stdio.h>                     /* sscanf */
 
 /* Utility library. */
@@ -67,6 +70,8 @@
 #include <connect.h>
 #include <split_at.h>
 #include <valid_hostname.h>
+#include <ip_match.h>
+#include <myaddrinfo.h>
 
 /* Global library. */
 
@@ -199,13 +204,15 @@ static void ps_dnsbl_add_site(const char *site)
 {
     const char *myname = "ps_dnsbl_add_site";
     char   *saved_site = mystrdup(site);
+    VSTRING *byte_codes = 0;
     PS_DNSBL_HEAD *head;
     PS_DNSBL_SITE *new_site;
     char    junk;
     const char *weight_text;
-    const char *pattern_text;
+    char *pattern_text;
     int     weight;
     HTABLE_INFO *ht;
+    char   *parse_err;
 
     /*
      * Parse the required DNSBL domain name, the optional reply filter and
@@ -221,11 +228,11 @@ static void ps_dnsbl_add_site(const char *site)
     } else {
        weight = 1;
     }
-    /* Preliminary fixed-string filter. */
+    /* Reply filter. */
     if ((pattern_text = split_at(saved_site, '=')) != 0) {
-       if (valid_ipv4_hostaddr(pattern_text, DO_GRIPE) == 0)
-           msg_fatal("bad DNSBL filter syntax \"%s\" in \"%s\"",
-                     pattern_text, site);
+       byte_codes = vstring_alloc(100);
+       if ((parse_err = ip_match_parse(byte_codes, pattern_text)) != 0)
+           msg_fatal("bad DNSBL filter syntax: %s", parse_err);
     }
     if (valid_hostname(saved_site, DO_GRIPE) == 0)
        msg_fatal("bad DNSBL domain name \"%s\" in \"%s\"",
@@ -256,26 +263,33 @@ static void ps_dnsbl_add_site(const char *site)
      * name.
      */
     new_site = (PS_DNSBL_SITE *) mymalloc(sizeof(*new_site));
-    new_site->filter = (pattern_text ? mystrdup(pattern_text) : 0);
+    new_site->filter = (pattern_text ? ip_match_save(byte_codes) : 0);
     new_site->weight = weight;
     new_site->next = head->first;
     head->first = new_site;
 
     myfree(saved_site);
+    if (byte_codes)
+       vstring_free(byte_codes);
 }
 
 /* ps_dnsbl_match - match DNSBL reply filter */
 
 static int ps_dnsbl_match(const char *filter, ARGV *reply)
 {
+    char    addr_buf[MAI_HOSTADDR_STRSIZE];
     char  **cpp;
 
     /*
-     * Preliminary fixed-string implementation.
+     * Run the replies through the pattern-matching engine.
      */
-    for (cpp = reply->argv; *cpp != 0; cpp++)
-       if (strcmp(filter, *cpp) == 0)
+    for (cpp = reply->argv; *cpp != 0; cpp++) {
+       if (inet_pton(AF_INET, *cpp, addr_buf) != 1)
+           msg_warn("address conversion error for %s -- ignoring this reply",
+                    *cpp);
+       if (ip_match_execute(filter, addr_buf))
            return (1);
+    }
     return (0);
 }
 
index 2e4af5022e906aa6c77e70f3881f626543d7950c..f3d6c42679f01a6255d981cdf7e5e2736ca26e39 100644 (file)
@@ -350,6 +350,8 @@ static int ps_data_cmd(PS_STATE *state, char *args)
      * so that we dont't have to deal with broken zombies that fall silent at
      * the first reject response. For now we rely on stress-dependent command
      * read timeouts.
+     * 
+     * If we proceed into the data phase, enforce over-all DATA time limit.
      */
     return (PS_SEND_REPLY(state,
                          "554 5.5.1 Error: no valid recipients\r\n"));
@@ -614,15 +616,14 @@ static void ps_smtpd_read_event(int event, char *context)
            }
 
            /*
-            * Yield this pseudo thread when the VSTREAM buffer is empty.
+            * Yield this pseudo thread when the VSTREAM buffer is empty in
+            * the middle of a command.
             * 
-            * Set a short per-command timeout if under stress.
+            * XXX Do not reset the read timeout. The entire command must be
+            * received within the time limit.
             */
-           if (PS_SMTPD_BUFFER_EMPTY(state)) {
-               event_request_timer(ps_smtpd_time_event, (char *) state,
-                                   PS_EFF_CMD_TIME_LIMIT);
+           if (PS_SMTPD_BUFFER_EMPTY(state))
                return;
-           }
        }
 
        /*
@@ -788,16 +789,17 @@ static void ps_smtpd_read_event(int event, char *context)
            return;
        }
 
+       /*
+        * Reset the command read timeout before reading the next command.
+        */
+       event_request_timer(ps_smtpd_time_event, (char *) state,
+                           PS_EFF_CMD_TIME_LIMIT);
+
        /*
         * Yield this pseudo thread when the VSTREAM buffer is empty.
-        * 
-        * Set a short per-command timeout if under stress.
         */
-       if (PS_SMTPD_BUFFER_EMPTY(state)) {
-           event_request_timer(ps_smtpd_time_event, (char *) state,
-                               PS_EFF_CMD_TIME_LIMIT);
+       if (PS_SMTPD_BUFFER_EMPTY(state))
            return;
-       }
     }
 }
 
index 7c7fdfe0b476984ed16eb3984ca6fbe2b8db9239..a281d4a502968a385ea67e077c68943d6ac51eb3 100644 (file)
@@ -163,8 +163,12 @@ void    ps_parse_tests(PS_STATE *state,
     unsigned long nsmtp_stamp;
     unsigned long barlf_stamp;
     unsigned long penal_stamp;
+
+#ifdef NONPROD
     time_t  penalty_left;
 
+#endif
+
     /*
      * We don't know what tests have expired or have never passed.
      */
index ef50fda86dbe3a1fc5d6b05aded7163171a5e97e..44bb071096c096f2677fb9b3f3bb6a2172528d86 100644 (file)
@@ -83,6 +83,7 @@ qmqpd.o: ../../include/netstring.h
 qmqpd.o: ../../include/quote_822_local.h
 qmqpd.o: ../../include/quote_flags.h
 qmqpd.o: ../../include/rec_type.h
+qmqpd.o: ../../include/recipient_list.h
 qmqpd.o: ../../include/record.h
 qmqpd.o: ../../include/sys_defs.h
 qmqpd.o: ../../include/vbuf.h
@@ -101,6 +102,7 @@ qmqpd_peer.o: ../../include/msg.h
 qmqpd_peer.o: ../../include/myaddrinfo.h
 qmqpd_peer.o: ../../include/mymalloc.h
 qmqpd_peer.o: ../../include/sock_addr.h
+qmqpd_peer.o: ../../include/split_at.h
 qmqpd_peer.o: ../../include/stringops.h
 qmqpd_peer.o: ../../include/sys_defs.h
 qmqpd_peer.o: ../../include/valid_hostname.h
index f026c9e8624cfb68b905869cdee13281b52c161c..a4bfe5fe734692f2c574c263518c0699514f66af 100644 (file)
@@ -57,6 +57,7 @@
 #include <myaddrinfo.h>
 #include <sock_addr.h>
 #include <inet_proto.h>
+#include <split_at.h>
 
 /* Global library. */
 
@@ -150,6 +151,14 @@ void    qmqpd_peer_init(QMQPD_STATE *state)
                      myname, MAI_STRERROR(aierr));
        state->port = mystrdup(client_port.buf);
 
+       /*
+        * XXX Strip off the IPv6 datalink suffix to avoid false alarms with
+        * strict address syntax checks.
+        */
+#ifdef HAS_IPV6
+       (void) split_at(client_addr.buf, '%');
+#endif
+
        /*
         * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
         * but only if IPv4 support is enabled (why would anyone want to turn
index 01bd2ef40f38cbbd5f64f476a7ea41da9154dafd..0273877d68e17cf89e8ac33ec8d2d9b61f6b2bd4 100644 (file)
@@ -255,6 +255,7 @@ smtpd_check.o: ../../include/inet_addr_list.h
 smtpd_check.o: ../../include/inet_proto.h
 smtpd_check.o: ../../include/input_transp.h
 smtpd_check.o: ../../include/iostuff.h
+smtpd_check.o: ../../include/ip_match.h
 smtpd_check.o: ../../include/is_header.h
 smtpd_check.o: ../../include/mac_expand.h
 smtpd_check.o: ../../include/mac_parse.h
@@ -344,6 +345,7 @@ smtpd_peer.o: ../../include/mymalloc.h
 smtpd_peer.o: ../../include/name_code.h
 smtpd_peer.o: ../../include/name_mask.h
 smtpd_peer.o: ../../include/sock_addr.h
+smtpd_peer.o: ../../include/split_at.h
 smtpd_peer.o: ../../include/stringops.h
 smtpd_peer.o: ../../include/sys_defs.h
 smtpd_peer.o: ../../include/tls.h
index 5e074a5097dd79d1c7a23bbeeb06034d17d3f170..8adc1f1a3ee087236970118d708a5c2a38f22a92 100644 (file)
@@ -3044,6 +3044,9 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      * XXX Should use something other than CLEANUP_STAT_WRITE when we lose
      * contact with the cleanup server. This requires changes to the
      * mail_stream module and its users (smtpd, qmqpd, perhaps sendmail).
+     * 
+     * XXX See exception below in code that overrides state->access_denied for
+     * compliance with RFC 2821 Sec 3.1.
      */
     if (smtpd_milters != 0 && (state->err & CLEANUP_STAT_WRITE) != 0)
        state->access_denied = mystrdup("421 4.3.0 Mail system error");
@@ -4521,6 +4524,11 @@ static void smtpd_proto(SMTPD_STATE *state)
            }
            /* XXX We use the real client for connect access control. */
            if (state->access_denied && cmdp->action != quit_cmd) {
+               /* XXX Exception for Milter override. */
+               if (strncmp(state->access_denied + 1, "21", 2) == 0) {
+                   smtpd_chat_reply(state, "%s", state->access_denied);
+                   continue;
+               }
                smtpd_chat_reply(state, "503 5.7.0 Error: access denied for %s",
                                 state->namaddr);       /* RFC 2821 Sec 3.1 */
                state->error_count++;
index 7bfc04335756f7cc5ea9715a5ba9222067a3c04f..c1b2330bffb84aaa578cfd29da18732a80a27e8c 100644 (file)
 #include <attr_clnt.h>
 #include <myaddrinfo.h>
 #include <inet_proto.h>
+#include <ip_match.h>
 
 /* DNS library. */
 
@@ -266,6 +267,7 @@ static jmp_buf smtpd_check_buf;
   */
 static VSTRING *error_text;
 static CTABLE *smtpd_rbl_cache;
+static CTABLE *smtpd_rbl_byte_cache;
 
  /*
   * Pre-opened SMTP recipient maps so we can reject mail for unknown users.
@@ -418,18 +420,20 @@ static int PRINTFLIKE(5, 6) smtpd_check_reject(SMTPD_STATE *, int, int, const ch
   */
 typedef struct {
     char   *txt;                       /* TXT content or NULL */
-    ARGV   *a;                         /* A records */
+    DNS_RR *a;                         /* A records */
 } SMTPD_RBL_STATE;
 
 static void *rbl_pagein(const char *, void *);
 static void rbl_pageout(void *, void *);
+static void *rbl_byte_pagein(const char *, void *);
+static void rbl_byte_pageout(void *, void *);
 
  /*
   * Context for RBL $name expansion.
   */
 typedef struct {
     SMTPD_STATE *state;                        /* general state */
-    const char *domain;                        /* query domain */
+    char *domain;                      /* query domain */
     const char *what;                  /* rejected value */
     const char *class;                 /* name of rejected value */
     const char *txt;                   /* randomly selected trimmed TXT rr */
@@ -639,6 +643,8 @@ void    smtpd_check_init(void)
      * sessions so we cannot make it dependent on session state.
      */
     smtpd_rbl_cache = ctable_create(100, rbl_pagein, rbl_pageout, (void *) 0);
+    smtpd_rbl_byte_cache = ctable_create(1000, rbl_byte_pagein,
+                                        rbl_byte_pageout, (void *) 0);
 
     /*
      * Pre-parse the restriction lists. At the same time, pre-open tables
@@ -2952,13 +2958,11 @@ static SMTPD_RBL_STATE dnsxl_stat_soft[1];
 
 static void *rbl_pagein(const char *query, void *unused_context)
 {
-    const char *myname = "rbl_pagein";
     DNS_RR *txt_list;
     VSTRING *why;
     int     dns_status;
     SMTPD_RBL_STATE *rbl = 0;
     DNS_RR *addr_list;
-    MAI_HOSTADDR_STR hostaddr;
     DNS_RR *rr;
     DNS_RR *next;
     VSTRING *buf;
@@ -3006,15 +3010,7 @@ static void *rbl_pagein(const char *query, void *unused_context)
        dns_rr_free(txt_list);
     } else
        rbl->txt = 0;
-    rbl->a = argv_alloc(1);
-    for (rr = addr_list; rr != 0; rr = rr->next) {
-       if (dns_rr_to_pa(rr, &hostaddr) == 0)
-           msg_warn("%s: skipping record type %s for query %s: %m",
-                    myname, dns_strtype(rr->type), query);
-       else
-           argv_add(rbl->a, hostaddr.buf, ARGV_END);
-    }
-    dns_rr_free(addr_list);
+    rbl->a = addr_list;
     return ((void *) rbl);
 }
 
@@ -3028,11 +3024,35 @@ static void rbl_pageout(void *data, void *unused_context)
        if (rbl->txt)
            myfree(rbl->txt);
        if (rbl->a)
-           argv_free(rbl->a);
+           dns_rr_free(rbl->a);
        myfree((char *) rbl);
     }
 }
 
+/* rbl_byte_pagein - parse RBL reply pattern, save byte codes */
+
+static void *rbl_byte_pagein(const char *query, void *unused_context)
+{
+    VSTRING *byte_codes = vstring_alloc(100);
+    char   *saved_query = mystrdup(query);
+    char   *saved_byte_codes;
+    char   *err;
+
+    if ((err = ip_match_parse(byte_codes, saved_query)) != 0)
+       msg_fatal("RBL reply error: %s", err);
+    saved_byte_codes = ip_match_save(byte_codes);
+    myfree(saved_query);
+    vstring_free(byte_codes);
+    return (saved_byte_codes);
+}
+
+/* rbl_byte_pageout - discard parsed RBL reply byte codes */
+
+static void rbl_byte_pageout(void *data, void *unused_context)
+{
+    myfree(data);
+}
+
 /* rbl_expand_lookup - RBL specific $name expansion */
 
 static const char *rbl_expand_lookup(const char *name, int mode,
@@ -3091,7 +3111,8 @@ static int rbl_reject_reply(SMTPD_STATE *state, const SMTPD_RBL_STATE *rbl,
     }
     why = vstring_alloc(100);
     rbl_exp.state = state;
-    rbl_exp.domain = rbl_domain;
+    rbl_exp.domain = mystrdup(rbl_domain);
+    (void) split_at(rbl_exp.domain, '=');
     rbl_exp.what = what;
     rbl_exp.class = reply_class;
     rbl_exp.txt = (rbl->txt == 0 ? "" : rbl->txt);
@@ -3138,6 +3159,7 @@ static int rbl_reject_reply(SMTPD_STATE *state, const SMTPD_RBL_STATE *rbl,
     /*
      * Clean up.
      */
+    myfree(rbl_exp.domain);
     vstring_free(why);
 
     return (result);
@@ -3145,13 +3167,20 @@ static int rbl_reject_reply(SMTPD_STATE *state, const SMTPD_RBL_STATE *rbl,
 
 /* rbl_match_addr - match address list */
 
-static int rbl_match_addr(SMTPD_RBL_STATE *rbl, const char *addr)
+static int rbl_match_addr(SMTPD_RBL_STATE *rbl, const char *byte_codes)
 {
-    char  **cpp;
+    const char *myname = "rbl_match_addr";
+    DNS_RR *rr;
 
-    for (cpp = rbl->a->argv; *cpp; cpp++)
-       if (strcmp(*cpp, addr) == 0)
-           return (1);
+    for (rr = rbl->a; rr != 0; rr = rr->next) {
+       if (rr->type == T_A) {
+           if (ip_match_execute(byte_codes, rr->data))
+               return (1);
+       } else {
+           msg_warn("%s: skipping record type %s for query %s",
+                    myname, dns_strtype(rr->type), rr->qname);
+       }
+    }
     return (0);
 }
 
@@ -3167,6 +3196,7 @@ static const SMTPD_RBL_STATE *find_dnsxl_addr(SMTPD_STATE *state,
     int     i;
     SMTPD_RBL_STATE *rbl;
     const char *reply_addr;
+    const char *byte_codes;
     struct addrinfo *res;
     unsigned char *ipv6_addr;
 
@@ -3210,12 +3240,14 @@ static const SMTPD_RBL_STATE *find_dnsxl_addr(SMTPD_STATE *state,
     vstring_strcat(query, rbl_domain);
     reply_addr = split_at(STR(query), '=');
     rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query));
+    if (reply_addr != 0)
+       byte_codes = ctable_locate(smtpd_rbl_byte_cache, reply_addr);
 
     /*
      * If the record exists, match the result address.
      */
     if (SMTPD_DNSXL_STAT_OK(rbl) && reply_addr != 0
-       && !rbl_match_addr(rbl, reply_addr))
+       && !rbl_match_addr(rbl, byte_codes))
        rbl = 0;
     vstring_free(query);
     return (rbl);
@@ -3284,6 +3316,7 @@ static const SMTPD_RBL_STATE *find_dnsxl_domain(SMTPD_STATE *state,
     SMTPD_RBL_STATE *rbl;
     const char *domain;
     const char *reply_addr;
+    const char *byte_codes;
 
     /*
      * Extract the domain, tack on the RBL domain name and query the DNS for
@@ -3302,12 +3335,14 @@ static const SMTPD_RBL_STATE *find_dnsxl_domain(SMTPD_STATE *state,
     vstring_sprintf(query, "%s.%s", domain, rbl_domain);
     reply_addr = split_at(STR(query), '=');
     rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query));
+    if (reply_addr != 0)
+       byte_codes = ctable_locate(smtpd_rbl_byte_cache, reply_addr);
 
     /*
      * If the record exists, match the result address.
      */
     if (SMTPD_DNSXL_STAT_OK(rbl) && reply_addr != 0
-       && !rbl_match_addr(rbl, reply_addr))
+       && !rbl_match_addr(rbl, byte_codes))
        rbl = 0;
     vstring_free(query);
     return (rbl);
index 25ca3d30674c52181315b50b37fc5ff67ef834d3..2aa9217748eeb01426068f16394357e70de0bacd 100644 (file)
 #include <myaddrinfo.h>
 #include <sock_addr.h>
 #include <inet_proto.h>
+#include <split_at.h>
 
 /* Global library. */
 
@@ -224,6 +225,14 @@ void    smtpd_peer_init(SMTPD_STATE *state)
                      myname, MAI_STRERROR(aierr));
        state->port = mystrdup(client_port.buf);
 
+       /*
+        * XXX Strip off the IPv6 datalink suffix to avoid false alarms with
+        * strict address syntax checks.
+        */
+#ifdef HAS_IPV6
+       (void) split_at(client_addr.buf, '%');
+#endif
+
        /*
         * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
         * but only if IPv4 support is enabled (why would anyone want to turn
index e79a4accdeddf82faac271c6f4faf3966f262c08..b07f74fdb863f66cd3b7bb172cebc6bae4252e9b 100644 (file)
@@ -32,7 +32,8 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \
        allascii.c load_file.c killme_after.c vstream_tweak.c upass_connect.c \
        upass_listen.c upass_trigger.c edit_file.c inet_windowsize.c \
-       unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c
+       unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c \
+       ip_match.c ip_lmatch.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -66,7 +67,8 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \
        allascii.o load_file.o killme_after.o vstream_tweak.o upass_connect.o \
        upass_listen.o upass_trigger.o edit_file.o inet_windowsize.o \
-       unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o
+       unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o \
+       ip_match.o ip_lmatch.o
 HDRS   = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
        dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
@@ -86,7 +88,8 @@ HDRS  = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        stringops.h sys_defs.h timed_connect.h timed_wait.h trigger.h \
        username.h valid_hostname.h vbuf.h vbuf_print.h vstream.h vstring.h \
        vstring_vstream.h watchdog.h format_tv.h load_file.h killme_after.h \
-       edit_file.h dict_cache.h dict_thash.h
+       edit_file.h dict_cache.h dict_thash.h \
+       ip_match.h ip_lmatch.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c test_send_fd test_recv_fd
 DEFS   = -I. -D$(SYSTYPE)
@@ -104,7 +107,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
        attr_scan0 host_port attr_scan_plain attr_print_plain htable \
        unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \
        myaddrinfo myaddrinfo4 inet_proto sane_basename format_tv \
-       test_send_fd test_recv_fd valid_utf_8
+       test_send_fd test_recv_fd valid_utf_8 ip_match ip_lmatch
 
 LIB_DIR        = ../../lib
 INC_DIR        = ../../include
@@ -147,7 +150,7 @@ shar:
        @shar $(FILES)
 
 lint:
-       lint $(SRCS)
+       lint $(DEFS) $(SRCS) $(LINTFIX)
 
 clean:
        rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp
@@ -419,11 +422,21 @@ valid_utf_8: $(LIB)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
        mv junk $@.o
 
+ip_match: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
+ip_lmatch: $(LIB)
+       mv $@.o junk
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+       mv junk $@.o
+
 tests: valid_hostname_test mac_expand_test dict_test unescape_test \
        hex_quote_test ctable_test inet_addr_list_test base64_code_test \
        attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
        dict_cidr_test attr_scan_plain_test htable_test hex_code_test \
-       myaddrinfo_test format_tv_test
+       myaddrinfo_test format_tv_test ip_match_test ip_lmatch_test
 
 root_tests:
 
@@ -545,6 +558,16 @@ format_tv_test: format_tv format_tv.in format_tv.ref
        diff format_tv.ref format_tv.tmp
        rm -f format_tv.tmp
 
+ip_match_test: ip_match ip_match.in ip_match.ref
+       ./ip_match <ip_match.in >ip_match.tmp
+       diff ip_match.ref ip_match.tmp
+       rm -f ip_match.tmp
+
+ip_lmatch_test: ip_lmatch ip_lmatch.in ip_lmatch.ref
+       ./ip_lmatch <ip_lmatch.in >ip_lmatch.tmp
+       diff ip_lmatch.ref ip_lmatch.tmp
+       rm -f ip_lmatch.tmp
+
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
        set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -1184,6 +1207,20 @@ inet_windowsize.o: inet_windowsize.c
 inet_windowsize.o: iostuff.h
 inet_windowsize.o: msg.h
 inet_windowsize.o: sys_defs.h
+ip_match.o: ip_match.c
+ip_match.o: ip_match.h
+ip_match.o: msg.h
+ip_match.o: mymalloc.h
+ip_match.o: sys_defs.h
+ip_match.o: vbuf.h
+ip_match.o: vstring.h
+ip_lmatch.o: ip_lmatch.c
+ip_lmatch.o: ip_lmatch.h
+ip_lmatch.o: msg.h
+ip_lmatch.o: mymalloc.h
+ip_lmatch.o: sys_defs.h
+ip_lmatch.o: vbuf.h
+ip_lmatch.o: vstring.h
 killme_after.o: killme_after.c
 killme_after.o: killme_after.h
 killme_after.o: sys_defs.h
diff --git a/postfix/src/util/ip_lmatch.c b/postfix/src/util/ip_lmatch.c
new file mode 100644 (file)
index 0000000..70dc1ec
--- /dev/null
@@ -0,0 +1,450 @@
+/*++
+/* NAME
+/*     ip_lmatch 3
+/* SUMMARY
+/*     lazy IP address pattern matching
+/* SYNOPSIS
+/*     #include <ip_lmatch.h>
+/*
+/*     int     ip_lmatch(pattern, addr, why)
+/*     char    *pattern;
+/*     const char *addr;
+/*     VSTRING **why;
+/* DESCRIPTION
+/*     This module supports IP address pattern matching. See below
+/*     for a description of the supported address pattern syntax.
+/*
+/*     This version optimizes for implementation convenience.  The
+/*     lazy parser stops as soon as the address does not match the
+/*     pattern. This results in a poor user interface: a pattern
+/*     syntax error at the end will be reported ONLY when an address
+/*     matches the entire pattern before the syntax error.
+/*
+/*     Use the ip_match() module for an implementation that has
+/*     separate parsing and matching stages. That implementation
+/*     reports a syntax error immediately, and provides faster
+/*     matching at the cost of a more complex programming interface.
+/*
+/*     ip_lmatch_parse() matches the address bytes while parsing
+/*     the pattern, and terminates as soon as a non-match or syntax
+/*     error is found.  The result is -1 in case of syntax error,
+/*     0 in case of no match, 1 in case of a match.
+/*
+/*     Arguments
+/* .IP addr
+/*     Network address in printable form.
+/* .IP pattern
+/*     Address pattern. This argument may be modified.
+/* .IP why
+/*     Pointer to storage for error reports (result value -1). If
+/*     the target is a null pointer, ip_lmatch() will allocate a
+/*     buffer that should be freed by the application.
+/* 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 comma-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 <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <myaddrinfo.h>
+#include <ip_lmatch.h>
+
+ /*
+  * Token values.
+  */
+#define IP_LMATCH_CODE_OPEN    '['     /* start of set */
+#define IP_LMATCH_CODE_CLOSE   ']'     /* end of set */
+#define IP_LMATCH_CODE_OVAL    'N'     /* octet value */
+#define IP_LMATCH_CODE_EOF     '\0'    /* oops */
+#define IP_LMATCH_CODE_ERR     256     /* oops */
+
+ /*
+  * Address length is protocol dependent. Find out how large our address byte
+  * strings should be.
+  */
+#ifdef HAS_IPV6
+#define IP_LMATCH_ABYTES       MAI_V6ADDR_BYTES
+#else
+#define IP_LMATCH_ABYTES       MAI_V4ADDR_BYTES
+#endif
+
+ /*
+  * SLMs.
+  */
+#define STR    vstring_str
+#define LEN    VSTRING_LEN
+
+/* ip_lmatch_next_token - carve out the next token from user input */
+
+static int ip_lmatch_next_token(char **pstart, char **psaved_start, int *poval)
+{
+    unsigned char *cp;
+    unsigned char *next;
+    int     oval;
+
+    /*
+     * Return a value-less token (i.e. a literal, error, or EOF.
+     */
+#define IP_LMATCH_RETURN_TOK(next, type) \
+    do { *pstart = (char *) (next); return (type); } while (0)
+
+    /*
+     * Return a token that contains an IPv4 address octet value.
+     */
+#define IP_LMATCH_RETURN_TOK_OVAL(next, oval) do { \
+       *poval = (oval); IP_LMATCH_RETURN_TOK((next), IP_LMATCH_CODE_OVAL); \
+    } 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';
+       for (next = cp + 1; ISDIGIT(*next); next++) {
+           oval *= 10;
+           oval += *next - '0';
+           if (oval > 255)
+               IP_LMATCH_RETURN_TOK(next + 1, IP_LMATCH_CODE_ERR);
+       }
+       IP_LMATCH_RETURN_TOK_OVAL(next, oval);
+    } else {
+       IP_LMATCH_RETURN_TOK(*cp ? cp + 1 : cp, *cp);
+    }
+}
+
+/* ip_lmatch_print_parse_error - report parsing error in context */
+
+static void PRINTFLIKE(5, 6) ip_lmatch_print_parse_error(VSTRING **why,
+                                                                char *start,
+                                                                char *here,
+                                                                char *next,
+                                                       const char *fmt,...)
+{
+    va_list ap;
+    int     start_width;
+    int     here_width;
+
+    /*
+     * On-the-fly allocation.
+     */
+    if (*why == 0)
+       *why = vstring_alloc(20);
+
+    /*
+     * Format the error type.
+     */
+    va_start(ap, fmt);
+    vstring_vsprintf(*why, 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.
+     */
+#define IP_LMATCH_NO_ERROR_CONTEXT (char *) 0, (char *) 0, (char *) 0
+
+    if (start != 0) {
+       start_width = here - start;
+       here_width = next - here;
+       vstring_sprintf_append(*why, " at \"%.*s>%.*s<%s\"",
+                              start_width, start_width == 0 ? "" : start,
+                            here_width, here_width == 0 ? "" : here, next);
+    }
+}
+
+/* ip_lmatch - match an address pattern */
+
+int     ip_lmatch(char *pattern, const char *addr, VSTRING **why)
+{
+    const char *myname = "ip_lmatch";
+    char    addr_bytes[IP_LMATCH_ABYTES];
+    const unsigned char *ap;
+    int     octet_count;
+    char   *saved_cp;
+    char   *cp;
+    int     token_type;
+    int     look_ahead;
+    int     oval;
+    int     saved_oval;
+    int     matched;
+
+    /*
+     * For now, IPv4 support only. Use different parser loops for IPv4 and
+     * IPv6.
+     */
+    switch (inet_pton(AF_INET, addr, addr_bytes)) {
+    case -1:
+       msg_fatal("%s: address conversion error: %m", myname);
+    case 0:
+       msg_warn("%s: unexpected address form: %s", myname, addr);
+       return (0);
+    }
+
+    /*
+     * Simplify this if we change to {} for "octet set" notation.
+     */
+#define FIND_TERMINATOR(start, cp) do { \
+       int _level = 1; \
+       for (cp = (start) ; *cp; cp++) { \
+           if (*cp == '[') _level++; \
+           if (*cp != ']') continue; \
+           if (--_level == 0) break; \
+       } \
+    } while (0)
+
+    /*
+     * Strip [] around the entire pattern.
+     */
+    if (*pattern == '[') {
+       FIND_TERMINATOR(pattern, cp);
+       if (cp[0] == 0) {
+           ip_lmatch_print_parse_error(why, IP_LMATCH_NO_ERROR_CONTEXT,
+                                       "missing \"]\" character");
+           return (-1);
+       }
+       if (cp[1] == 0) {
+           *cp = 0;
+           pattern += 1;
+       }
+    }
+
+    /*
+     * Sanity check. In this case we can't show any error context.
+     */
+    if (*pattern == 0) {
+       ip_lmatch_print_parse_error(why, IP_LMATCH_NO_ERROR_CONTEXT,
+                                   "empty address pattern");
+       return (-1);
+    }
+
+    /*
+     * Simple on-the-fly pattern matching.
+     */
+    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 (ap = (const unsigned char *) addr_bytes; /* void */ ; ap++) {
+       switch (token_type = ip_lmatch_next_token(&cp, &saved_cp, &oval)) {
+
+           /*
+            * Numeric address field.
+            */
+       case IP_LMATCH_CODE_OVAL:
+           if (*ap == oval)
+               break;
+           return (0);
+
+           /*
+            * Wild-card address field.
+            */
+       case IP_LMATCH_CODE_OPEN:
+           matched = 0;
+           /* Require comma-separated numbers or numeric ranges. */
+           for (;;) {
+               token_type = ip_lmatch_next_token(&cp, &saved_cp, &oval);
+               if (token_type == IP_LMATCH_CODE_OVAL) {
+                   saved_oval = oval;
+                   look_ahead = ip_lmatch_next_token(&cp, &saved_cp, &oval);
+                   /* Numeric range. */
+                   if (look_ahead == '.') {
+                       /* Brute-force parsing. */
+                       if (ip_lmatch_next_token(&cp, &saved_cp, &oval) == '.'
+                           && ip_lmatch_next_token(&cp, &saved_cp, &oval)
+                           == IP_LMATCH_CODE_OVAL
+                           && saved_oval <= oval) {
+                           if (!matched)
+                               matched = (*ap >= saved_oval && *ap <= oval);
+                           look_ahead =
+                               ip_lmatch_next_token(&cp, &saved_cp, &oval);
+                       } else {
+                           ip_lmatch_print_parse_error(why, pattern,
+                                                       saved_cp, cp,
+                                                    "numeric range error");
+                           return (-1);
+                       }
+                   }
+                   /* Single number. */
+                   else {
+                       if (!matched)
+                           matched = (*ap == oval);
+                   }
+                   /* Require "," or end-of-wildcard. */
+                   token_type = look_ahead;
+                   if (token_type == ',') {
+                       continue;
+                   } else if (token_type == IP_LMATCH_CODE_CLOSE) {
+                       break;
+                   } else {
+                       ip_lmatch_print_parse_error(why, pattern, saved_cp, cp,
+                                                   "need \",\" or \"%c\"",
+                                                   IP_LMATCH_CODE_CLOSE);
+                       return (-1);
+                   }
+               } else {
+                   ip_lmatch_print_parse_error(why, pattern, saved_cp, cp,
+                                             "need decimal number 0..255");
+                   return (-1);
+               }
+           }
+           if (matched == 0)
+               return (0);
+           break;
+
+           /*
+            * Invalid field.
+            */
+       default:
+           ip_lmatch_print_parse_error(why, pattern, saved_cp, cp,
+                                    "need decimal number 0..255 or \"%c\"",
+                                       IP_LMATCH_CODE_OPEN);
+           return (-1);
+       }
+       octet_count += 1;
+
+       /*
+        * Require four address fields. Not one more, not one less.
+        */
+       if (octet_count == 4) {
+           if (*cp != 0) {
+               (void) ip_lmatch_next_token(&cp, &saved_cp, &oval);
+               ip_lmatch_print_parse_error(why, pattern, saved_cp, cp,
+                                           "garbage after pattern");
+               return (-1);
+           }
+           return (1);
+       }
+
+       /*
+        * Require "." before the next address field.
+        */
+       if (ip_lmatch_next_token(&cp, &saved_cp, &oval) != '.') {
+           ip_lmatch_print_parse_error(why, pattern, saved_cp, cp,
+                                       "need \".\"");
+           return (-1);
+       }
+    }
+}
+
+#ifdef TEST
+
+ /*
+  * Dummy main program for regression tests.
+  */
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+
+int     main(int argc, char **argv)
+{
+    VSTRING *why = vstring_alloc(100);
+    VSTRING *line_buf = vstring_alloc(100);
+    char   *bufp;
+    char   *user_pattern;
+    char   *user_address;
+    int     echo_input = !isatty(0);
+    int     match_status;
+
+    /*
+     * Iterate over the input stream. The input format is a pattern, followed
+     * by 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;
+
+       /*
+        * Match the patterns.
+        */
+       while ((user_address = mystrtok(&bufp, " \t")) != 0) {
+           match_status = ip_lmatch(user_pattern, user_address, &why);
+           if (match_status < 0) {
+               vstream_printf("Error: %s\n", STR(why));
+           } else {
+               vstream_printf("Match %s: %s\n", user_address,
+                              match_status ? "yes" : "no");
+           }
+           vstream_fflush(VSTREAM_OUT);
+       }
+    }
+    vstring_free(line_buf);
+    vstring_free(why);
+    exit(0);
+}
+
+#endif
diff --git a/postfix/src/util/ip_lmatch.h b/postfix/src/util/ip_lmatch.h
new file mode 100644 (file)
index 0000000..788956f
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _IP_LMATCH_H_INCLUDED_
+#define _IP_LMATCH_H_INCLUDED_
+
+/*++
+/* NAME
+/*     ip_lmatch 3h
+/* SUMMARY
+/*     lazy IP address pattern matching
+/* SYNOPSIS
+/*     #include <ip_lmatch.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+extern int ip_lmatch(char *, const char *, VSTRING **);
+
+/* 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
+/*--*/
+
+#endif
diff --git a/postfix/src/util/ip_lmatch.in b/postfix/src/util/ip_lmatch.in
new file mode 100644 (file)
index 0000000..9b7aba3
--- /dev/null
@@ -0,0 +1,21 @@
+1.2.3.4                1.2.3.4
+1.2.300.4      1.2.3.4
+1.2.3.         1.2.3.4
+1.2.3          1.2.3.4
+a              1.2.3.4
+1.2.3,4                1.2.3.4
+1.2.[3].4      1.2.3.4
+1.2.[].4       1.2.3.4
+1.2.[.4                1.2.3.4
+1.2.].4                1.2.3.4
+1.2.[1..127,128..255].5        1.2.3.4
+1.2.[1-255].5  1.2.3.4
+1.2.[1..127.128..255].5        1.2.3.4
+1.2.3.[4]      1.2.3.4
+1.2.3.[4..1]   1.2.3.4
+1.2.3.[4.1]    1.2.3.4
+1.2.3.[4.x]    1.2.3.4
+1.2.3.[x]      1.2.3.4
+1.2.3.4x       1.2.3.4
+1.2.[3..11].5  1.2.3.5 1.2.2.5 1.2.11.5 1.2.12.5  1.2.11.6
+1.2.[3,5,7,9,11].5     1.2.3.5 1.2.2.5 1.2.4.5 1.2.11.5 1.2.12.5  1.2.11.6
diff --git a/postfix/src/util/ip_lmatch.ref b/postfix/src/util/ip_lmatch.ref
new file mode 100644 (file)
index 0000000..d8431bd
--- /dev/null
@@ -0,0 +1,51 @@
+> 1.2.3.4              1.2.3.4
+Match 1.2.3.4: yes
+> 1.2.300.4    1.2.3.4
+Error: need decimal number 0..255 or "[" at "1.2.>300<.4"
+> 1.2.3.               1.2.3.4
+Error: need decimal number 0..255 or "[" at "1.2.3.><"
+> 1.2.3                1.2.3.4
+Error: need "." at "1.2.3><"
+> a            1.2.3.4
+Error: need decimal number 0..255 or "[" at ">a<"
+> 1.2.3,4              1.2.3.4
+Error: need "." at "1.2.3>,<4"
+> 1.2.[3].4    1.2.3.4
+Match 1.2.3.4: yes
+> 1.2.[].4     1.2.3.4
+Error: need decimal number 0..255 at "1.2.[>]<.4"
+> 1.2.[.4              1.2.3.4
+Error: need decimal number 0..255 at "1.2.[>.<4"
+> 1.2.].4              1.2.3.4
+Error: need decimal number 0..255 or "[" at "1.2.>]<.4"
+> 1.2.[1..127,128..255].5      1.2.3.4
+Match 1.2.3.4: no
+> 1.2.[1-255].5        1.2.3.4
+Error: need "," or "]" at "1.2.[1>-<255].5"
+> 1.2.[1..127.128..255].5      1.2.3.4
+Error: need "," or "]" at "1.2.[1..127>.<128..255].5"
+> 1.2.3.[4]    1.2.3.4
+Match 1.2.3.4: yes
+> 1.2.3.[4..1] 1.2.3.4
+Error: numeric range error at "1.2.3.[4..>1<]"
+> 1.2.3.[4.1]  1.2.3.4
+Error: numeric range error at "1.2.3.[4.>1<]"
+> 1.2.3.[4.x]  1.2.3.4
+Error: numeric range error at "1.2.3.[4.>x<]"
+> 1.2.3.[x]    1.2.3.4
+Error: need decimal number 0..255 at "1.2.3.[>x<]"
+> 1.2.3.4x     1.2.3.4
+Error: garbage after pattern at "1.2.3.4>x<"
+> 1.2.[3..11].5        1.2.3.5 1.2.2.5 1.2.11.5 1.2.12.5  1.2.11.6
+Match 1.2.3.5: yes
+Match 1.2.2.5: no
+Match 1.2.11.5: yes
+Match 1.2.12.5: no
+Match 1.2.11.6: no
+> 1.2.[3,5,7,9,11].5   1.2.3.5 1.2.2.5 1.2.4.5 1.2.11.5 1.2.12.5  1.2.11.6
+Match 1.2.3.5: yes
+Match 1.2.2.5: no
+Match 1.2.4.5: no
+Match 1.2.11.5: yes
+Match 1.2.12.5: no
+Match 1.2.11.6: no
diff --git a/postfix/src/util/ip_match.c b/postfix/src/util/ip_match.c
new file mode 100644 (file)
index 0000000..17d1db2
--- /dev/null
@@ -0,0 +1,675 @@
+/*++
+/* 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 translating
+/*     the pattern to 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 comma-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;
+    unsigned char *next;
+    int     oval;
+
+    /*
+     * 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_OVAL(next, oval) do { \
+       *poval = (oval); IP_MATCH_RETURN_TOK((next), IP_MATCH_CODE_OVAL); \
+    } 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';
+       for (next = cp + 1; ISDIGIT(*next); next++) {
+           oval *= 10;
+           oval += *next - '0';
+           if (oval > 255)
+               IP_MATCH_RETURN_TOK(next + 1, IP_MATCH_CODE_ERR);
+       }
+       IP_MATCH_RETURN_TOK_OVAL(next, oval);
+    } else {
+       IP_MATCH_RETURN_TOK(*cp ? cp + 1 : cp, *cp);
+    }
+}
+
+/* ipmatch_print_parse_error - formatted parsing error, with context */
+
+static void PRINTFLIKE(5, 6) 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 = 1; \
+       for (cp = (start) ; *cp; cp++) { \
+           if (*cp == '[') _level++; \
+           if (*cp != ']') continue; \
+           if (--_level == 0) break; \
+       } \
+    } 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 comma-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
diff --git a/postfix/src/util/ip_match.h b/postfix/src/util/ip_match.h
new file mode 100644 (file)
index 0000000..781a04a
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _IP_MATCH_H_INCLUDED_
+#define _IP_MATCH_H_INCLUDED_
+
+/*++
+/* NAME
+/*     ip_match 3h
+/* SUMMARY
+/*     IP address pattern matching
+/* SYNOPSIS
+/*     #include <ip_match.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+extern char *ip_match_parse(VSTRING *, char *);
+extern char *ip_match_save(const VSTRING *);
+extern char *ip_match_dump(VSTRING *, const char *);
+extern int ip_match_execute(const char *, const char *);
+
+/* 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
+/*--*/
+
+#endif
diff --git a/postfix/src/util/ip_match.in b/postfix/src/util/ip_match.in
new file mode 100644 (file)
index 0000000..97964fb
--- /dev/null
@@ -0,0 +1,21 @@
+1.2.3.4
+1.2.300.4
+1.2.3.
+1.2.3
+a
+1.2.3,4
+1.2.[3].4
+1.2.[].4
+1.2.[.4
+1.2.].4
+1.2.[1..127,128..255].5
+1.2.[1-255].5
+1.2.[1..127.128..255].5
+1.2.3.[4]
+1.2.3.[4..1]
+1.2.3.[4.1]
+1.2.3.[4.x]
+1.2.3.[x]
+1.2.3.4x
+1.2.[3..11].5  1.2.3.5 1.2.2.5 1.2.11.5 1.2.12.5  1.2.11.6
+1.2.[3,5,7,9,11].5     1.2.3.5 1.2.2.5 1.2.4.5 1.2.11.5 1.2.12.5  1.2.11.6
diff --git a/postfix/src/util/ip_match.ref b/postfix/src/util/ip_match.ref
new file mode 100644 (file)
index 0000000..ba75840
--- /dev/null
@@ -0,0 +1,53 @@
+> 1.2.3.4
+Code: 1.2.3.4
+> 1.2.300.4
+Error: need decimal number 0..255 or "[" at "1.2.>300<.4"
+> 1.2.3.
+Error: need decimal number 0..255 or "[" at "1.2.3.><"
+> 1.2.3
+Error: need "." at "1.2.3><"
+> a
+Error: need decimal number 0..255 or "[" at ">a<"
+> 1.2.3,4
+Error: need "." at "1.2.3>,<4"
+> 1.2.[3].4
+Code: 1.2.[3].4
+> 1.2.[].4
+Error: need decimal number 0..255 at "1.2.[>]<.4"
+> 1.2.[.4
+Error: need decimal number 0..255 at "1.2.[>.<4"
+> 1.2.].4
+Error: need decimal number 0..255 or "[" at "1.2.>]<.4"
+> 1.2.[1..127,128..255].5
+Code: 1.2.[1..127,128..255].5
+> 1.2.[1-255].5
+Error: need "," or "]" at "1.2.[1>-<255].5"
+> 1.2.[1..127.128..255].5
+Error: need "," or "]" at "1.2.[1..127>.<128..255].5"
+> 1.2.3.[4]
+Code: 1.2.3.[4]
+> 1.2.3.[4..1]
+Error: numeric range error at "1.2.3.[4..>1<]"
+> 1.2.3.[4.1]
+Error: numeric range error at "1.2.3.[4.>1<]"
+> 1.2.3.[4.x]
+Error: numeric range error at "1.2.3.[4.>x<]"
+> 1.2.3.[x]
+Error: need decimal number 0..255 at "1.2.3.[>x<]"
+> 1.2.3.4x
+Error: garbage after pattern at "1.2.3.4>x<"
+> 1.2.[3..11].5        1.2.3.5 1.2.2.5 1.2.11.5 1.2.12.5  1.2.11.6
+Code: 1.2.[3..11].5
+Match 1.2.3.5: yes
+Match 1.2.2.5: no
+Match 1.2.11.5: yes
+Match 1.2.12.5: no
+Match 1.2.11.6: no
+> 1.2.[3,5,7,9,11].5   1.2.3.5 1.2.2.5 1.2.4.5 1.2.11.5 1.2.12.5  1.2.11.6
+Code: 1.2.[3,5,7,9,11].5
+Match 1.2.3.5: yes
+Match 1.2.2.5: no
+Match 1.2.4.5: no
+Match 1.2.11.5: yes
+Match 1.2.12.5: no
+Match 1.2.11.6: no
index 185199c9c77a7b64fd63d2994e765818c437c48a..5be02e4491e566dfc6656057fce8b18eb2cec1d8 100644 (file)
@@ -14,7 +14,7 @@
 /*
 /*     Arguments:
 /* .IP fd
-/*     File descriptor.
+/*     File descriptor that connects the sending and receiving processes.
 /* DIAGNOSTICS
 /*     stream_recv_fd() returns -1 upon failure.
 /* LICENSE
index 26116d886882edb23624e6a93ba01e83ed9c31df..0a9aebf79f8da65bfdb36b5248f2ca666ff774df 100644 (file)
@@ -15,9 +15,9 @@
 /*
 /*     Arguments:
 /* .IP fd
-/*     File descriptor.
+/*     File descriptor that connects the sending and receiving processes.
 /* .IP sendfd
-/*     Another file descriptor.
+/*     The file descriptor to be sent.
 /* DIAGNOSTICS
 /*     stream_send_fd() returns -1 upon failure.
 /* LICENSE
index cb6c8aaffe3f00c5b547dd50e78d4a089559544e..33ab387ca1ef26e0bcf3ed19432b4ece092ceaa7 100644 (file)
@@ -14,7 +14,7 @@
 /*
 /*     Arguments:
 /* .IP fd
-/*     File descriptor.
+/*     File descriptor that connects the sending and receiving processes.
 /* DIAGNOSTICS
 /*     unix_recv_fd() returns -1 upon failure.
 /* LICENSE
index bb6bd128213ba44cb89c23eede50bbad90cc249a..8cbade1201c32a9b8f59f2a3f72342b0b9d2e15b 100644 (file)
@@ -15,9 +15,9 @@
 /*
 /*     Arguments:
 /* .IP fd
-/*     File descriptor.
+/*     File descriptor that connects the sending and receiving processes.
 /* .IP sendfd
-/*     Another file descriptor.
+/*     The file descriptor to be sent.
 /* DIAGNOSTICS
 /*     unix_send_fd() returns -1 upon failure.
 /* LICENSE