spf-discuss
[Top] [All Lists]

new ABNF for the SPF spec (Was: nailing down the CIDR length)

2005-07-10 10:51:49

I mentioned a while back when I found a utility to convert ABNF to
regular expressions that I found some problems with the current ABNF
for the SPF records.  One of the problems I found was that the CIDR
length wasn't nailed down.


I have created some updated ABNF that I would like to bounce off
everyone before I even consider putting it into the spec.

The changes are:

* The definition for the unknown-modifier has been changed slightly to
  note that unknown modifiers MUST NOT match "redirect" and "exp".
  The current ABNF technically allows this and since the "redirect"
  and "exp" modifiers require <domain-spec>s instead of
  <macro-strings>, things like "redirect=1.2.3.4" will, technically,
  match the unknown-modifier case.

  If anyone knows how to easily say "match <name> except if it is
  'redirect' or 'exp'" in ABNF, please let me know.  I know it is
  technically possible, but you end up with REs that start with stuff
  like:
     
[a-z0-9_.-]|[a-qs-z][a-z0-9_.-]*|[a-z][a-z0-9_.-]|[a-z][a-df-z0-9_.-]|[a-z][a-z0-9_.-]
 ... probably around 1000 chars deleted ...


* I have created a series of ANBF terms in the form of range$i-$j
  which validate only integers in the range of $i to $j.  These ABNF
  terms are used in the CIDR lengths, the IPv4 addresses, and the macro
  variable truncation number.

* The old ABNF allowed things invalid TLDs like "a:foo.b%-"


Here is the new suggested ABNF for SPF records:

   record           = version terms *SP
   version          = "v=spf1"

   terms            = *( 1*SP ( directive / modifier ) )

   directive        = [ qualifier ] mechanism
   qualifier        = "+" / "-" / "?" / "~"
   mechanism        = ( all / include
                      / A / MX / PTR / IP4 / IP6 / exists )

   all              = "all"
   include          = "include"  ":" domain-spec
   A                = "a"      [ ":" domain-spec ] [ dual-cidr-length ]
   MX               = "mx"     [ ":" domain-spec ] [ dual-cidr-length ]
   PTR              = "ptr"    [ ":" domain-spec ]
   IP4              = "ip4"      ":" ip4-network   [ ip4-cidr-length ]
   IP6              = "ip6"      ":" ip6-network   [ ip6-cidr-length ]
   exists           = "exists"   ":" domain-spec

   modifier         = redirect / explanation / unknown-modifier
   redirect         = "redirect" "=" domain-spec
   explanation      = "exp" "=" domain-spec
   unknown-modifier = unknown-mod-name "=" macro-string
   unknown-mod-name = <the same as 'name' except it MUST NOT match "redirect"
                       or "exp">  ; this can not be easily expressed in ABNF
                     

   ip4-cidr-length  = "/" range1-32
   ip6-cidr-length  = "/" range1-128

   ; integers restricted to a given range
   range1-9         = %x31-39                    ;   1 ..   9
   range1-32        = range1-9                   ;   1 ..   9
                      / "1" DIGIT                ;  10 ..  19
                      / "2" DIGIT                ;  20 ..  29
                      / "3" %x30-32              ;  30 ..  32
   range1-128       = range1-9 [ DIGIT ]         ;   1 ..  99
                      / "10" DIGIT               ; 100 .. 109
                      / "11" DIGIT               ; 110 .. 119
                      / "12" %x30-38             ; 120 .. 128
   range0-255       = DIGIT                      ;   0 .. 9
                      / range1-9 DIGIT           ;  10 .. 99
                      / "1" 2DIGIT               ; 100 .. 199
                      / "2" %x30-34 DIGIT        ; 200 .. 249
                      / "25" %x30-35             ; 250 .. 255


   dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ]

   ip4-network      = range0-255 "." range0-255 "." range0-255 "." range0-255
             ; conventional dotted quad notation.  e.g. 192.0.2.0
   ip6-network      = <as per [RFC 3513], section 2.2>
             ; e.g. 2001:DB8::CD30


   domain-spec      = macro-string domain-end
   domain-end       = ( "." toplabel ) / macro-var
   toplabel         = ALPHA / ALPHA *[ alphanum / "-" ] alphanum
                      ; LDH rule (See [RFC3696])
   alphanum         = ALPHA / DIGIT

   explain-string   = *( macro-string / SP )

   macro-string     = *( macro-expand / macro-literal )
   macro-var        = "%{" macro-letter transformers *delimiter "}"
   macro-expand     = macro-var / "%%" / "%_" / "%-"
   macro-literal    = %x21-24 / %x26-7E
                      ; visible characters except "%"
   macro-letter     = "s" / "l" / "o" / "d" / "i" / "p" / "h" /
                      "c" / "r" / "t"
   transformers     = [ range1-128 ] [ "r" ]
   delimiter        = "." / "-" / "+" / "," / "/" / "_" / "="

   name             = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )


Here is a diff:

--- spf_rec_abnf-02.rb  2005-07-10 01:12:26.000000000 -0500
+++ spf_rec_abnf-03.rb  2005-07-10 12:51:00.000000000 -0500
@@ -30,25 +30,41 @@
    modifier         = redirect / explanation / unknown-modifier
    redirect         = "redirect" "=" domain-spec
    explanation      = "exp" "=" domain-spec
-   unknown-modifier = name "=" macro-string
+   unknown-modifier = unknown-mod-name "=" macro-string
+   unknown-mod-name = <the same as 'name' except it MUST NOT match "redirect"
+                       or "exp">  ; this can not be easily expressed in ABNF
+                     
+
+   ip4-cidr-length  = "/" range1-32
+   ip6-cidr-length  = "/" range1-128
+
+   ; integers restricted to a given range
+   range1-9         = %x31-39                    ;   1 ..   9
+   range1-32        = range1-9                   ;   1 ..   9
+                      / "1" DIGIT                ;  10 ..  19
+                      / "2" DIGIT                ;  20 ..  29
+                      / "3" %x30-32              ;  30 ..  32
+   range1-128       = range1-9 [ DIGIT ]         ;   1 ..  99
+                      / "10" DIGIT               ; 100 .. 109
+                      / "11" DIGIT               ; 110 .. 119
+                      / "12" %x30-38             ; 120 .. 128
+   range0-255       = DIGIT                      ;   0 .. 9
+                      / range1-9 DIGIT           ;  10 .. 99
+                      / "1" 2DIGIT               ; 100 .. 199
+                      / "2" %x30-34 DIGIT        ; 200 .. 249
+                      / "25" %x30-35             ; 250 .. 255
+
 
-   ip4-cidr-length  = "/" 1*DIGIT
-   ip6-cidr-length  = "/" 1*DIGIT
    dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ]
 
-   ip4-network      = qnum "." qnum "." qnum "." qnum
-   qnum             = DIGIT                 ; 0-9
-                      / %x31-39 DIGIT       ; 10-99
-                      / "1" 2DIGIT          ; 100-199
-                      / "2" %x30-34 DIGIT   ; 200-249
-                      / "25" %x30-35        ; 250-255
+   ip4-network      = range0-255 "." range0-255 "." range0-255 "." range0-255
              ; conventional dotted quad notation.  e.g. 192.0.2.0
 ;   ip6-network      = <as per [RFC 3513], section 2.2>
 ;             ; e.g. 2001:DB8::CD30
 
 
    domain-spec      = macro-string domain-end
-   domain-end       = ( "." toplabel ) / macro-expand
+   domain-end       = ( "." toplabel ) / macro-var
    toplabel         = ALPHA / ALPHA *[ alphanum / "-" ] alphanum
                       ; LDH rule (See [RFC3696])
    alphanum         = ALPHA / DIGIT
@@ -56,13 +72,13 @@
    explain-string   = *( macro-string / SP )
 
    macro-string     = *( macro-expand / macro-literal )
-   macro-expand     = ( "%{" macro-letter transformers *delimiter "}" )
-                      / "%%" / "%_" / "%-"
+   macro-var        = "%{" macro-letter transformers *delimiter "}"
+   macro-expand     = macro-var / "%%" / "%_" / "%-"
    macro-literal    = %x21-24 / %x26-7E
                       ; visible characters except "%"
    macro-letter     = "s" / "l" / "o" / "d" / "i" / "p" / "h" /
                       "c" / "r" / "t"
-   transformers     = *DIGIT [ "r" ]
+   transformers     = [ range1-128 ] [ "r" ]
    delimiter        = "." / "-" / "+" / "," / "/" / "_" / "="
 
    name             = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )



-wayne


<Prev in Thread] Current Thread [Next in Thread>