3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Directly processes text of dos.xml.
19 DosXmlParser is called with an XML string to produce a list of BlackListEntry
20 objects containing the data from the XML.
22 DosXmlParser: converts XML to list of BlackListEntrys.
23 BlacklistEntry: describes a blacklisted IP.
27 from xml
.etree
import ElementTree
31 from google
.appengine
.tools
import xml_parser_utils
32 from google
.appengine
.tools
.app_engine_config_exception
import AppEngineConfigException
34 MISSING_SUBNET
= '<blacklist> node must have a subnet specified'
35 BAD_IPV_SUBNET
= '"%s" is not a valid IPv4 or IPv6 subnet'
36 BAD_PREFIX_LENGTH
= ('Prefix length of subnet "%s" must be an integer '
37 '(quad-dotted masks are not supported)')
40 def GetDosYaml(unused_application
, dos_xml_str
):
41 return _MakeDosListIntoYaml(DosXmlParser().ProcessXml(dos_xml_str
))
44 def _MakeDosListIntoYaml(dos_list
):
45 """Converts yaml statement list of blacklisted IP's into a string."""
46 statements
= ['blacklist:']
47 for entry
in dos_list
:
48 statements
+= entry
.ToYaml()
49 return '\n'.join(statements
) + '\n'
52 class DosXmlParser(object):
53 """Provides logic for walking down XML tree and pulling data."""
55 def ProcessXml(self
, xml_str
):
56 """Parses XML string and returns object representation of relevant info.
59 xml_str: The XML string.
61 A list of BlacklistEntry objects containing information about blacklisted
62 IP's specified in the XML.
64 AppEngineConfigException: In case of malformed XML or illegal inputs.
68 self
.blacklist_entries
= []
70 xml_root
= ElementTree
.fromstring(xml_str
)
71 if xml_root
.tag
!= 'blacklistentries':
72 raise AppEngineConfigException('Root tag must be <blacklistentries>')
74 for child
in xml_root
.getchildren():
75 self
.ProcessBlacklistNode(child
)
78 raise AppEngineConfigException('\n'.join(self
.errors
))
80 return self
.blacklist_entries
81 except ElementTree
.ParseError
:
82 raise AppEngineConfigException('Bad input -- not valid XML')
84 def ProcessBlacklistNode(self
, node
):
85 """Processes XML <blacklist> nodes into BlacklistEntry objects.
87 The following information is parsed out:
88 subnet: The IP, in CIDR notation.
89 description: (optional)
90 If there are no errors, the data is loaded into a BlackListEntry object
91 and added to a list. Upon error, a description of the error is added to
92 a list and the method terminates.
95 node: <blacklist> XML node in dos.xml.
97 tag
= xml_parser_utils
.GetTag(node
)
98 if tag
!= 'blacklist':
99 self
.errors
.append('Unrecognized node: <%s>' % tag
)
102 entry
= BlacklistEntry()
103 entry
.subnet
= xml_parser_utils
.GetChildNodeText(node
, 'subnet')
104 entry
.description
= xml_parser_utils
.GetChildNodeText(node
, 'description')
106 validation
= self
._ValidateEntry
(entry
)
108 self
.errors
.append(validation
)
110 self
.blacklist_entries
.append(entry
)
112 def _ValidateEntry(self
, entry
):
114 return MISSING_SUBNET
116 ipaddr
.IPNetwork(entry
.subnet
)
118 return BAD_IPV_SUBNET
% entry
.subnet
119 parts
= entry
.subnet
.split('/')
120 if len(parts
) == 2 and not re
.match('^[0-9]+$', parts
[1]):
121 return BAD_PREFIX_LENGTH
% entry
.subnet
124 class BlacklistEntry(object):
125 """Instances contain information about individual blacklist entries."""
128 statements
= ['- subnet: %s' % self
.subnet
]
131 ' description: %s' % self
._SanitizeForYaml
(self
.description
))
134 def _SanitizeForYaml(self
, dirty_str
):
135 return "'%s'" % dirty_str
.replace('\n', ' ')