App Engine Python SDK version 1.8.9
[gae.git] / python / google / appengine / tools / dos_xml_parser.py
blob96de44ead13678797518aa88ab93eaccac1fa664
1 #!/usr/bin/env python
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.
24 """
26 import re
27 from xml.etree import ElementTree
29 import ipaddr
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.
58 Args:
59 xml_str: The XML string.
60 Returns:
61 A list of BlacklistEntry objects containing information about blacklisted
62 IP's specified in the XML.
63 Raises:
64 AppEngineConfigException: In case of malformed XML or illegal inputs.
65 """
67 try:
68 self.blacklist_entries = []
69 self.errors = []
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)
77 if self.errors:
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.
94 Args:
95 node: <blacklist> XML node in dos.xml.
96 """
97 tag = xml_parser_utils.GetTag(node)
98 if tag != 'blacklist':
99 self.errors.append('Unrecognized node: <%s>' % tag)
100 return
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)
107 if validation:
108 self.errors.append(validation)
109 return
110 self.blacklist_entries.append(entry)
112 def _ValidateEntry(self, entry):
113 if not entry.subnet:
114 return MISSING_SUBNET
115 try:
116 ipaddr.IPNetwork(entry.subnet)
117 except ValueError:
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."""
127 def ToYaml(self):
128 statements = ['- subnet: %s' % self.subnet]
129 if self.description:
130 statements.append(
131 ' description: %s' % self._SanitizeForYaml(self.description))
132 return statements
134 def _SanitizeForYaml(self, dirty_str):
135 return "'%s'" % dirty_str.replace('\n', ' ')