Index: debian/changelog =================================================================== --- debian/changelog (Revision 13639) +++ debian/changelog (Arbeitskopie) @@ -1,3 +1,105 @@ +univention-directory-manager-modules (5.1.13-1) unstable; urgency=low + + * merged with trunk v5.0.45-1 (Bug #15687) + + -- Kai-Wilhelm Bolte Fri, 13 Nov 2009 13:49:52 +0100 + +univention-directory-manager-modules (5.1.12-2) unstable; urgency=low + + * udm now stores IPv6 addresses as aAAARecord for forward zones in LDAP + (Bug #15687) + + -- Kai-Wilhelm Bolte Fri, 13 Nov 2009 12:27:41 +0100 + +univention-directory-manager-modules (5.1.12-1) unstable; urgency=low + + * merged with trunk (Bug #15687) + + -- Kai-Wilhelm Bolte Wed, 4 Nov 2009 14:04:51 +0100 + +univention-directory-manager-modules (5.1.11-1) unstable; urgency=low + + * make DNS filters IPv6-capable (Bug #15687) + + -- Kai-Wilhelm Bolte Wed, 4 Nov 2009 11:48:49 +0100 + +univention-directory-manager-modules (5.1.10-1) unstable; urgency=low + + * small bugfixes in syntax check (Bug #15555) + + -- Kai-Wilhelm Bolte Tue, 13 Oct 2009 09:39:47 +0200 + +univention-directory-manager-modules (5.1.9-1) unstable; urgency=low + + * use ipaddr module for syntax check (Bug #15555) + * fixed typos + + -- Kai-Wilhelm Bolte Mon, 12 Oct 2009 14:08:31 +0200 + +univention-directory-manager-modules (5.1.8-1) unstable; urgency=low + + * German translations update in syntax (Bug #15555) + + -- Kai-Wilhelm Bolte Fri, 9 Oct 2009 12:22:16 +0200 + +univention-directory-manager-modules (5.1.7-1) unstable; urgency=low + + * IPv6 forward and reverse zones are now correctly stored in ldap (Bug #15687) + * fixes in DNS syntax check and add another IPv6 syntax check + * first version which uses new ipaddr module + + -- Kai-Wilhelm Bolte Thu, 8 Oct 2009 11:51:26 +0200 + +univention-directory-manager-modules (5.1.6-1) unstable; urgency=low + + * add IPv6 to 'ipAdress' syntax check + * bugfixes in IPv6 DNS-handling (Bug #15687) + + -- Kai-Wilhelm Bolte Mon, 28 Sep 2009 14:42:14 +0200 + +univention-directory-manager-modules (5.1.5-1) unstable; urgency=low + + * add IPv6 syntax check for DNS and some comments (Bug #15687) + + -- Kai-Wilhelm Bolte Tue, 22 Sep 2009 09:19:30 +0200 + +univention-directory-manager-modules (5.1.4-1) unstable; urgency=low + + * alias, forward_zone and ptr_record are now IPv6-ready ( Bug #15687) + * bugfixes in reverse_zone and host_record + + -- Kai-Wilhelm Bolte Mon, 21 Sep 2009 14:09:35 +0200 + +univention-directory-manager-modules (5.1.3-1) unstable; urgency=low + + * udm reverse_zone is now IPv6-ready (Bug #15687) + * add syntax-checking for IPv6-subnets + * nicer code in dns/host_record + + -- Kai-Wilhelm Bolte Fri, 18 Sep 2009 13:26:19 +0200 + +univention-directory-manager-modules (5.1.2-1) unstable; urgency=low + + * removed aaaa in UDM (Bug #15687) + * udm dns/host_record create and modify now handles aRecord and aAAARecord + attributes in ldap depending on IP address version + * udm dns/host_record modify ... --append and --remove checks also IP address + version, --set replaces all old entries + + -- Kai-Wilhelm Bolte Mon, 14 Sep 2009 15:54:16 +0200 + +univention-directory-manager-modules (5.1.1-1) unstable; urgency=low + + * add possibility to create AAAA records with UDM: --set aaaa="IPv6-address" (Bug #15687) + + -- Kai-Wilhelm Bolte Fri, 4 Sep 2009 17:32:34 +0200 + +univention-directory-manager-modules (5.1.0-1) unstable; urgency=low + + * add possibility to add IPv6 addresses and check syntax for IPv4 / IPv6 in module computers (Bug #15555) + + -- Kai-Wilhelm Bolte Wed, 2 Sep 2009 11:56:53 +0200 + univention-directory-manager-modules (5.0.45-1) unstable; urgency=low * fix some more policy translation bugs (Bug: #14815) Index: modules/univention/admin/handlers/dns/alias.py =================================================================== --- modules/univention/admin/handlers/dns/alias.py (Revision 13639) +++ modules/univention/admin/handlers/dns/alias.py (Arbeitskopie) @@ -143,12 +143,19 @@ def lookup(co, lo, filter_s, base='', superordinate=None, scope="sub", unique=0, required=0, timeout=-1, sizelimit=0): - filter=univention.admin.filter.conjunction('&', [ + filter=univention.admin.filter.conjunction('|', [ + univention.admin.filter.conjunction('&', [ univention.admin.filter.expression('objectClass', 'dNSZone'), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.in-addr.arpa')]), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('ARecord', '*')]), - univention.admin.filter.expression('CNAMERecord', '*') + univention.admin.filter.expression('CNAMERecord', '*')]), + univention.admin.filter.conjunction('&', [ + univention.admin.filter.expression('objectClass', 'dNSZone'), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.ip6.arpa')]), # IPv6 + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('AAAARecord', '*')]), + univention.admin.filter.expression('CNAMERecord', '*')]) ]) if superordinate: @@ -165,12 +172,12 @@ return res def identify(dn, attr, canonical=0): - + # IPv4 + IPv6 return 'dNSZone' in attr.get('objectClass', []) and\ '@' not in attr.get('relativeDomainName', []) and\ - not attr['zoneName'][0].endswith('.in-addr.arpa') and\ + not attr['zoneName'][0].endswith('.arpa') and\ '*' in attr.get('CNAMERecord', []) and\ - not '*' in attr.get('ARecord', []) + not ('*' in attr.get('ARecord', []) or '*' in attr.get('AAAARecord', [])) def lookup_alias_filter(lo, filter_s): _re=re.compile('(.*)\(dnsAlias=([^=,]+)\)(.*)') Index: modules/univention/admin/handlers/dns/forward_zone.py =================================================================== --- modules/univention/admin/handlers/dns/forward_zone.py (Revision 13639) +++ modules/univention/admin/handlers/dns/forward_zone.py (Arbeitskopie) @@ -265,7 +265,8 @@ def _ldap_modlist(self): ml=univention.admin.handlers.simpleLdap._ldap_modlist(self) if self.hasChanged(['nameserver', 'contact', 'serial', 'refresh', 'retry', 'expire', 'ttl']): - ipaddr = re.compile ('^([0-9]{1,3}\.){3}[0-9]{1,3}$') # matches ip addresses - they shouldn't end with a dot! + # match IPv4 OR IPv6 address (incl. IPv4-mapped IPv6), they shouldn't end with a dot + ipaddr = re.compile ('^((0|[3-9][0-9]?|2(?:5[0-5]|[0-4]?[0-9])?|1[0-9]{0,2})(\.(0|[3-9][0-9]?|2(?:5[0-5]|[0-4]?[0-9])?|1[0-9]{0,2})){3})|((?!.*?::.*?::)[0-9a-fA-F]{0,4}(?:(?:(? 0 \ and ipaddr.match (self['nameserver'][0]) == None \ and self['nameserver'][0].find (':') == -1 \ @@ -283,10 +284,15 @@ def lookup(co, lo, filter_s, base='', superordinate=None, scope='sub', unique=0, required=0, timeout=-1, sizelimit=0): - filter=univention.admin.filter.conjunction('&', [ + filter=univention.admin.filter.conjunction('|', [ + univention.admin.filter.conjunction('&', [ univention.admin.filter.expression('objectClass', 'dNSZone'), univention.admin.filter.expression('relativeDomainName', '@'), - univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.in-addr.arpa')]) + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.in-addr.arpa')])]), + univention.admin.filter.conjunction('&', [ + univention.admin.filter.expression('objectClass', 'dNSZone'), + univention.admin.filter.expression('relativeDomainName', '@'), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.ip6.arpa')])]) # IPv6 ]) if filter_s: @@ -304,4 +310,4 @@ return 'dNSZone' in attr.get('objectClass', []) and\ ['@'] == attr.get('relativeDomainName', []) and\ - not attr['zoneName'][0].endswith('.in-addr.arpa') + not attr['zoneName'][0].endswith('.arpa') # IPv6 + IPv4 Index: modules/univention/admin/handlers/dns/ptr_record.py =================================================================== --- modules/univention/admin/handlers/dns/ptr_record.py (Revision 13639) +++ modules/univention/admin/handlers/dns/ptr_record.py (Arbeitskopie) @@ -123,10 +123,15 @@ def lookup(co, lo, filter_s, base='', superordinate=None,scope="sub", unique=0, required=0, timeout=-1, sizelimit=0): - filter=univention.admin.filter.conjunction('&', [ + filter=univention.admin.filter.conjunction('|', [ + univention.admin.filter.conjunction('&', [ univention.admin.filter.expression('objectClass', 'dNSZone'), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), - univention.admin.filter.expression('zoneName', '*.in-addr.arpa') + univention.admin.filter.expression('zoneName', '*.in-addr.arpa')]), + univention.admin.filter.conjunction('&', [ + univention.admin.filter.expression('objectClass', 'dNSZone'), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), + univention.admin.filter.expression('zoneName', '*.ip6.arpa')]) # IPv6 ]) if superordinate: @@ -146,4 +151,4 @@ return 'dNSZone' in attr.get('objectClass', []) and\ '@' not in attr.get('relativeDomainName', []) and\ - attr['zoneName'][0].endswith('.in-addr.arpa') + attr['zoneName'][0].endswith('.arpa') # IPv4 + IPv6 Index: modules/univention/admin/handlers/dns/reverse_zone.py =================================================================== --- modules/univention/admin/handlers/dns/reverse_zone.py (Revision 13639) +++ modules/univention/admin/handlers/dns/reverse_zone.py (Arbeitskopie) @@ -174,18 +174,38 @@ ] def mapSubnet(subnet): - q=subnet.split('.') - q.reverse() - return string.join(q, '.')+'.in-addr.arpa' - + v=[] + if ':' in subnet: # IPv6 + q=subnet.split(':') + for t in q: + u=t.zfill(4) + v.append((u)) + [v.append('0000') for x in v if len(v) < 4] + v=list(''.join(v)) + v.reverse() + return string.join(v, '.')+'.ip6.arpa' + else: + q=subnet.split('.') + q.reverse() + return string.join(q, '.')+'.in-addr.arpa' + def unmapSubnet(zone): if type(zone) == types.ListType: zone=zone[0] - zone=zone.replace('.in-addr.arpa', '') - q=zone.split('.') - q.reverse() - return string.join(q, '.') + if zone.find('ip6')!=-1: # IPv6 + zone=zone.replace('.ip6.arpa', '') + q=zone.split('.') + q.reverse() + q=[''.join(q[0:4]),''.join(q[4:8]),''.join(q[8:12]),''.join(q[12:16])] + return string.join(q, ':') + + else: + zone=zone.replace('.in-addr.arpa', '') + q=zone.split('.') + q.reverse() + return string.join(q, '.') + mapping=univention.admin.mapping.mapping() mapping.register('subnet', 'zoneName', mapSubnet, unmapSubnet) mapping.register('zonettl','dNSTTL', None, univention.admin.mapping.ListToString) @@ -263,11 +283,16 @@ def lookup(co, lo, filter_s, base='', superordinate=None, scope='sub', unique=0, required=0, timeout=-1, sizelimit=0): - filter=univention.admin.filter.conjunction('&', [ - univention.admin.filter.expression('objectClass', 'dNSZone'), - univention.admin.filter.expression('relativeDomainName', '@'), - univention.admin.filter.expression('zoneName', '*.in-addr.arpa') - ]) + filter=univention.admin.filter.conjunction('|', [ + univention.admin.filter.conjunction('&', [ + univention.admin.filter.expression('objectClass', 'dNSZone'), + univention.admin.filter.expression('relativeDomainName', '@'), + univention.admin.filter.expression('zoneName', '*.in-addr.arpa')]), + univention.admin.filter.conjunction('&', [ + univention.admin.filter.expression('objectClass', 'dNSZone'), + univention.admin.filter.expression('relativeDomainName', '@'), + univention.admin.filter.expression('zoneName', '*.ip6.arpa')]) # IPv6 + ]) if filter_s: filter_p=univention.admin.filter.parse(filter_s) @@ -283,7 +308,7 @@ return 'dNSZone' in attr.get('objectClass', []) and\ ['@'] == attr.get('relativeDomainName', []) and\ - attr['zoneName'][0].endswith('.in-addr.arpa') + attr['zoneName'][0].endswith('.arpa') # IPv4 + IPv6 def quickDescription(rdn): Index: modules/univention/admin/handlers/dns/srv_record.py =================================================================== --- modules/univention/admin/handlers/dns/srv_record.py (Revision 13639) +++ modules/univention/admin/handlers/dns/srv_record.py (Arbeitskopie) @@ -163,9 +163,9 @@ filter=univention.admin.filter.conjunction('&', [ univention.admin.filter.expression('objectClass', 'dNSZone'), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), - univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.in-addr.arpa')]), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.arpa')]), univention.admin.filter.expression('sRVRecord', '*'), - ]) + ]) # IPv4 + IPv6 if superordinate: filter.expressions.append(univention.admin.filter.expression('zoneName', superordinate.mapping.mapValue('zone', superordinate['zone']))) @@ -184,4 +184,4 @@ return 'dNSZone' in attr.get('objectClass', []) and\ '@' not in attr.get('relativeDomainName', []) and\ - not attr['zoneName'][0].endswith('.in-addr.arpa') + not attr['zoneName'][0].endswith('.arpa') # IPv4 + IPv6 Index: modules/univention/admin/handlers/dns/host_record.py =================================================================== --- modules/univention/admin/handlers/dns/host_record.py (Revision 13639) +++ modules/univention/admin/handlers/dns/host_record.py (Arbeitskopie) @@ -129,7 +129,6 @@ mapping=univention.admin.mapping.mapping() mapping.register('name', 'relativeDomainName', None, univention.admin.mapping.ListToString) -mapping.register('a', 'aRecord') mapping.register('mx', 'mXRecord', mapMX, unmapMX) mapping.register('txt', 'tXTRecord') mapping.register('zonettl', 'dNSTTL', None, univention.admin.mapping.ListToString) @@ -158,6 +157,15 @@ univention.admin.handlers.simpleLdap.__init__(self, co, lo, position, dn, superordinate) + def open(self): # IPv6 + univention.admin.handlers.simpleLdap.open(self) + self.info['a']=[] + if self.oldattr.has_key('aRecord'): + self.info['a'].extend((self.oldattr.get('aRecord'))) + if self.oldattr.has_key('aAAARecord'): + self.info['a'].extend((self.oldattr.get('aAAARecord'))) + self.save() # safe current state as old state + def exists(self): return self._exists @@ -170,6 +178,34 @@ (self.superordinate.mapping.mapName('zone'), self.superordinate.mapping.mapValue('zone', self.superordinate['zone'])), ] + def _ldap_modlist(self): # IPv6 + ml=univention.admin.handlers.simpleLdap._ldap_modlist(self) + old = self.oldinfo.get('a') + new = self.info.get('a') + ao=[] + an=[] + aaaao=[] + aaaan=[] + + if old != new: + if old: + for addr in old: + if ':' in addr: # IPv6 + aaaao.append((addr)) + else: + ao.append((addr)) + i=i+1 + if new: + for addr in new: + if ':' in addr: # IPv6 + aaaan.append((addr)) + else: + an.append((addr)) + ml.append(('aRecord', ao, an)) + ml.append(('aAAARecord', aaaao, aaaan)) + #print 'DEBUG: ml: "%s" ' %ml + return ml + def _ldap_post_create(self): self._updateZone() @@ -182,13 +218,20 @@ def lookup(co, lo, filter_s, base='', superordinate=None,scope="sub", unique=0, required=0, timeout=-1, sizelimit=0): - filter=univention.admin.filter.conjunction('&', [ + filter=univention.admin.filter.conjunction('|', [ + univention.admin.filter.conjunction('&', [ univention.admin.filter.expression('objectClass', 'dNSZone'), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.in-addr.arpa')]), univention.admin.filter.conjunction('!', [univention.admin.filter.expression('CNAMERecord', '*')]), - univention.admin.filter.conjunction('!', [univention.admin.filter.expression('SRVRecord', '*')]) - ]) + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('SRVRecord', '*')])]), + univention.admin.filter.conjunction('&', [ + univention.admin.filter.expression('objectClass', 'dNSZone'), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('relativeDomainName', '@')]), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('zoneName', '*.ip6.arpa')]), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('CNAMERecord', '*')]), + univention.admin.filter.conjunction('!', [univention.admin.filter.expression('SRVRecord', '*')])]) + ]) # IPv6 if superordinate: filter.expressions.append(univention.admin.filter.expression('zoneName', superordinate.mapping.mapValue('zone', superordinate['zone']))) @@ -206,9 +249,11 @@ def identify(dn, attr, canonical=0): univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'ALIAS(host_record) identify DN=%s'% dn) + # IPv4 + IPv6 return 'dNSZone' in attr.get('objectClass', []) and\ '@' not in attr.get('relativeDomainName', []) and\ - not attr['zoneName'][0].endswith('.in-addr.arpa') and\ + not attr['zoneName'][0].endswith('.arpa') and\ '*' not in attr.get('CNAMERecord', []) and\ '*' not in attr.get('SRVRecord', []) and\ - ('*' in attr.get('ARecord', []) or '*' in attr.get('MXRecord', []) ) + ('*' in attr.get('ARecord', []) or '*' in attr.get('AAAARecord', []) or '*' in attr.get('MXRecord', []) ) + Index: modules/univention/admin/handlers/__init__.py =================================================================== --- modules/univention/admin/handlers/__init__.py (Revision 13639) +++ modules/univention/admin/handlers/__init__.py (Arbeitskopie) @@ -29,6 +29,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import copy, types, sys, re, string, ldap +import ipaddr # IPv6 address handling import univention.debug import univention.admin.filter import univention.admin.uldap @@ -983,13 +984,21 @@ # HELPER def __ip_from_ptr( self, zoneName, relativeDomainName ): - zoneName = zoneName.replace( '.in-addr.arpa', '' ).split( '.' ) - zoneName.reverse( ) - relativeDomainName = relativeDomainName.split( '.' ) - relativeDomainName.reverse( ) + if zoneName.find('ip6')!=-1: # IPv6 + zoneName=zoneName.replace('.ip6.arpa', '').split( '.' ) + zoneName.reverse( ) + zoneName=(''.join(zoneName[0:4]) + ':' + ''.join(zoneName[4:8]) + ':' + ''.join(zoneName[8:12]) + ':' + ''.join(zoneName[12:16])) + relativeDomainName = relativeDomainName.split( '.' ) + relativeDomainName.reverse( ) + relativeDomainName=(''.join(relativeDomainName[0:4]) + ':' + ''.join(relativeDomainName[4:8]) + ':' + ''.join(relativeDomainName[8:12]) + ':' + ''.join(relativeDomainName[12:16])) + return '%s:%s' % ( zoneName, relativeDomainName ) + else: + zoneName = zoneName.replace( '.in-addr.arpa', '' ).split( '.' ) + zoneName.reverse( ) + relativeDomainName = relativeDomainName.split( '.' ) + relativeDomainName.reverse( ) + return '%s.%s' % ( string.join( zoneName, '.' ) , string.join( relativeDomainName, '.' ) ) - return '%s.%s' % ( string.join( zoneName, '.' ) , string.join( relativeDomainName, '.' ) ) - def __is_mac( self, mac ): _re = re.compile( '^[ 0-9a-fA-F ][ 0-9a-fA-F ]??$' ) m = mac.split( ':' ) @@ -1001,12 +1010,14 @@ return False def __is_ip( self, ip ): - _re = re.compile( '^[ 0-9 ]+\.[ 0-9 ]+\.[ 0-9 ]+\.[ 0-9 ]+$' ) - if _re.match ( ip ): - univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'IP[%s]? -> Yes' % ip ) - return True - univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'IP[%s]? -> No' % ip ) - return False + # return True if valid IPv4 (0.0.0.0 is allowed) or IPv6 address + try: + ipaddr.IPAddress(ip) + except ValueError: + univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'IP[%s]? -> No' % ip ) + return False + univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'IP[%s]? -> Yes' % ip ) + return True def open( self ): simpleLdap.open( self ) @@ -1362,16 +1373,29 @@ univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'we should create a dns reverse object: zoneDn="%s", name="%s", ip="%s"' % ( zoneDn, name, ip ) ) if name and zoneDn and ip: univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'dns reverse object: start' ) - subnet = ldap.explode_dn( zoneDn, 1 )[ 0 ].replace( '.in-addr.arpa', '' ).split( '.' ) - subnet.reverse( ) - subnet = string.join( subnet, '.' ) + '.' - ipPart = ip.replace( subnet, '' ) - if ipPart == ip: - raise univention.admin.uexceptions.missingInformation, _( 'Reverse zone and IP address are incompatible.' ) - - pointer = string.split( ipPart, '.' ) - pointer.reverse( ) - ipPart = string.join( pointer, '.' ) + if ip.find(':')!=-1: # IPv6, e.g. ip=2001:db8:100::5 + subnet = ldap.explode_dn( zoneDn, 1 )[ 0 ].replace( '.ip6.arpa', '' ).split( '.' ) + subnet.reverse( ) + subnet=[''.join(subnet[0:4]),''.join(subnet[4:8]),''.join(subnet[8:12]),''.join(subnet[12:16])] + subnet = string.join( subnet, ':' ) + ':' # e.g. '2001:0db8:0100:0000:' + ip6=(ipaddr.IPv6Address(ip)).exploded # use Python Module ipaddr to 'explode' IPv6 address + ipPart = ip6.replace( subnet, '' )# e.g. '0000:0000:0000:0005' + if ipPart == ip: + raise univention.admin.uexceptions.missingInformation, _( 'Reverse zone and IP address are incompatible.' ) + pointer = string.split( ipPart, ':' ) + pointer=list(''.join(pointer)) # e.g. ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '5'] + pointer.reverse() + ipPart=string.join(pointer, '.') # e.g. '5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0' + else: + subnet = ldap.explode_dn( zoneDn, 1 )[ 0 ].replace( '.in-addr.arpa', '' ).split( '.' ) + subnet.reverse( ) + subnet = string.join( subnet, '.' ) + '.' + ipPart = ip.replace( subnet, '' ) + if ipPart == ip: + raise univention.admin.uexceptions.missingInformation, _( 'Reverse zone and IP address are incompatible.' ) + pointer = string.split( ipPart, '.' ) + pointer.reverse( ) + ipPart = string.join( pointer, '.' ) tmppos = univention.admin.uldap.position( self.position.getDomain( ) ) # check in which forward zone the ip is set hostname_list = [] @@ -1485,34 +1509,61 @@ def __add_dns_forward_object( self, name, zoneDn, ip ): univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'we should add a dns forward object: zoneDn="%s", name="%s", ip="%s"' % ( zoneDn, name, ip ) ) - if name and ip and zoneDn: - results = self.lo.search( base = zoneDn, scope = 'domain', attr = [ 'aRecord' ], filter = '(&(relativeDomainName=%s)(!(cNAMERecord=*)))' % ( name ), unique = 0 ) - if not results: - try: - self.lo.add( 'relativeDomainName=%s,%s'% ( name, zoneDn ), [\ + if ip.find(':')!=-1: #IPv6 + if name and ip and zoneDn: + results = self.lo.search( base = zoneDn, scope = 'domain', attr = [ 'aAAARecord' ], filter = '(&(relativeDomainName=%s)(!(cNAMERecord=*)))' % ( name ), unique = 0 ) + if not results: + try: + self.lo.add( 'relativeDomainName=%s,%s'% ( name, zoneDn ), [\ ( 'objectClass', [ 'top', 'dNSZone' ]),\ ( 'zoneName', univention.admin.uldap.explodeDn( zoneDn, 1 )[ 0 ]),\ + ( 'aAAARecord', [ ip ]),\ + ( 'relativeDomainName', [ name ])]) + except univention.admin.uexceptions.objectExists: + raise univention.admin.uexceptions.dnsAliasRecordExists + + # TODO: check if zoneDn really a forwardZone, maybe it is a container under a zone + zone = univention.admin.handlers.dns.forward_zone.object( self.co, self.lo, self.position, zoneDn ) + zone.open() + zone.modify() + else: + for dn, attr in results: + if attr.has_key( 'aAAARecord' ): + new_ip_list = copy.deepcopy( attr[ 'aAAARecord' ] ) + if not ip in new_ip_list: + new_ip_list.append( ip ) + self.lo.modify( dn, [ ( 'aAAARecord', attr[ 'aAAARecord' ], new_ip_list ) ] ) + else: + self.lo.modify( dn, [ ( 'aAAARecord', '' , ip ) ] ) + pass + else: + if name and ip and zoneDn: + results = self.lo.search( base = zoneDn, scope = 'domain', attr = [ 'aRecord' ], filter = '(&(relativeDomainName=%s)(!(cNAMERecord=*)))' % ( name ), unique = 0 ) + if not results: + try: + self.lo.add( 'relativeDomainName=%s,%s'% ( name, zoneDn ), [\ + ( 'objectClass', [ 'top', 'dNSZone' ]),\ + ( 'zoneName', univention.admin.uldap.explodeDn( zoneDn, 1 )[ 0 ]),\ ( 'ARecord', [ ip ]),\ ( 'relativeDomainName', [ name ])]) - except univention.admin.uexceptions.objectExists: - raise univention.admin.uexceptions.dnsAliasRecordExists + except univention.admin.uexceptions.objectExists: + raise univention.admin.uexceptions.dnsAliasRecordExists - # TODO: check if zoneDn really a forwardZone, maybe it is a container under a zone - zone = univention.admin.handlers.dns.forward_zone.object( self.co, self.lo, self.position, zoneDn ) - zone.open() - zone.modify() - else: - for dn, attr in results: - if attr.has_key( 'aRecord' ): - new_ip_list = copy.deepcopy( attr[ 'aRecord' ] ) - if not ip in new_ip_list: - new_ip_list.append( ip ) - self.lo.modify( dn, [ ( 'aRecord', attr[ 'aRecord' ], new_ip_list ) ] ) - else: - self.lo.modify( dn, [ ( 'aRecord', '' , ip ) ] ) - pass + # TODO: check if zoneDn really a forwardZone, maybe it is a container under a zone + zone = univention.admin.handlers.dns.forward_zone.object( self.co, self.lo, self.position, zoneDn ) + zone.open() + zone.modify() + else: + for dn, attr in results: + if attr.has_key( 'aRecord' ): + new_ip_list = copy.deepcopy( attr[ 'aRecord' ] ) + if not ip in new_ip_list: + new_ip_list.append( ip ) + self.lo.modify( dn, [ ( 'aRecord', attr[ 'aRecord' ], new_ip_list ) ] ) + else: + self.lo.modify( dn, [ ( 'aRecord', '' , ip ) ] ) + pass - def __add_dns_alias_object( self, name, dnsForwardZone, dnsAliasZoneContainer, alias ): univention.debug.debug( univention.debug.ADMIN, univention.debug.INFO, 'add a dns alias object: name="%s", dnsForwardZone="%s", dnsAliasZoneContainer="%s", alias="%s"' % ( name, dnsForwardZone, dnsAliasZoneContainer, alias ) ) if name and dnsForwardZone and dnsAliasZoneContainer and alias: @@ -1814,38 +1865,75 @@ return ml def calc_dns_reverse_entry_name(self, sip, reverseDN): - subnet=ldap.explode_dn(reverseDN, 1)[0].replace('.in-addr.arpa','').split('.') - ip=sip.split('.') - zoneNet=subnet - zoneNet.reverse() - length=len(zoneNet) - count=0 - match=1 - for i in zoneNet: - if count == length: - break + if sip.find(':')!=-1: # IPv6 + subnet=ldap.explode_dn(reverseDN, 1)[0].replace('.ip6.arpa','').split('.') + ip6=ipaddr.IPv6Address(sip) # use Python Module ipaddr to 'explode' IPv6 address + ip6=ip6.exploded + ip6=ip6.split(':') + ip6=list(''.join(ip6)) + zoneNet=subnet + zoneNet.reverse() + length=len(zoneNet) + count=0 + match=1 + for i in zoneNet: + if count == length: + break - if not ip[count] == i: - match=0 - count += 1 + if not ip6[count] == i: + match=0 + count += 1 - if match == 1: - stop=(4-length) - rmIP='' - ip.reverse() + if match == 1: + stop=(32-length) + rmIP='' + ip6.reverse() + count=0 + for i in ip6: + if count == stop: + break + if len(rmIP) > 0: + rmIP=rmIP+'.'+ip6[count] + else: + rmIP=ip6[count] + count += 1 + return rmIP + else: + return 0 + + else: + subnet=ldap.explode_dn(reverseDN, 1)[0].replace('.in-addr.arpa','').split('.') + ip=sip.split('.') + zoneNet=subnet + zoneNet.reverse() + length=len(zoneNet) count=0 - for i in ip: - if count == stop: + match=1 + for i in zoneNet: + if count == length: break - if len(rmIP) > 0: - rmIP=rmIP+'.'+ip[count] - else: - rmIP=ip[count] - count=count+1 - return rmIP - else: - return 0 + if not ip[count] == i: + match=0 + count += 1 + + if match == 1: + stop=(4-length) + rmIP='' + ip.reverse() + count=0 + for i in ip: + if count == stop: + break + if len(rmIP) > 0: + rmIP=rmIP+'.'+ip[count] + else: + rmIP=ip[count] + count=count+1 + return rmIP # for ip='10.200.2.5' and subnet='2.200.10.in-addr.arpa' -> rmIP='5' ('5.2' for 200.10.in-addr.arpa) + else: + return 0 + def _ldap_pre_create(self): self.check_common_name_length() Index: modules/univention/admin/de.po =================================================================== --- modules/univention/admin/de.po (Revision 13639) +++ modules/univention/admin/de.po (Arbeitskopie) @@ -6,8 +6,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-08-07 14:05+0200\n" -"PO-Revision-Date: 2009-08-07 10:29+0200\n" +"POT-Creation-Date: 2009-11-04 13:48+0100\n" +"PO-Revision-Date: 2009-11-04 14:01+0100\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" @@ -20,23 +20,23 @@ msgid ": type was %s" msgstr ": Typ war %s" -#: modules.py:163 modules.py:346 +#: modules.py:166 modules.py:353 msgid "Custom" msgstr "Benutzerdefiniert" -#: modules.py:832 +#: modules.py:839 msgid "Search" msgstr "Suchen" -#: modules.py:832 +#: modules.py:839 msgid "Search object(s)" msgstr "Objekt(e) suchen" -#: modules.py:832 +#: modules.py:839 msgid "Add" msgstr "Hinzufügen" -#: modules.py:832 +#: modules.py:839 msgid "Add object(s)" msgstr "Objekt(e) hinzufügen" @@ -181,80 +181,79 @@ msgid "cannot find fqdn of " msgstr "FQDN konnte nicht gefunden werden: " -#: syntax.py:104 +#: syntax.py:105 msgid "not enough arguments" msgstr "Nicht genug Parameter" -#: syntax.py:108 +#: syntax.py:109 msgid "too many arguments" msgstr "Zu viele Parameter" -#: syntax.py:118 +#: syntax.py:119 msgid "Invalid syntax" msgstr "Falsche Syntax" -#: syntax.py:237 +#: syntax.py:247 msgid "Value must be a number!" msgstr "Der Wert muss eine Zahl sein." -#: syntax.py:249 +#: syntax.py:259 msgid "Value must be 0 or 1" msgstr "Der Wert muss 0 oder 1 sein." -#: syntax.py:261 +#: syntax.py:271 msgid "" "Value must be an integer followed by one of GB,MB,KB,B or nothing (equals B)!" msgstr "" "Der Wert muss eine Zahl sein. Optional kann noch eine Einheit mittels GB,MB," "KB oder B angegeben werden." -#: syntax.py:268 +#: syntax.py:278 msgid "Value may not contain whitespace or exclamation mark !" msgstr "Der Wert darf keine Leerzeichen oder Ausrufezeichen enthalten." -#: syntax.py:275 +#: syntax.py:285 msgid "undefined" msgstr "nicht definiert" -#: syntax.py:276 +#: syntax.py:286 msgid "mails" msgstr "Mails" -#: syntax.py:277 +#: syntax.py:287 msgid "events" msgstr "Kalender" -#: syntax.py:278 +#: syntax.py:288 msgid "contacts" msgstr "Kontakte" -#: syntax.py:279 +#: syntax.py:289 msgid "tasks" msgstr "Aufgaben" -#: syntax.py:280 +#: syntax.py:290 msgid "notes" msgstr "Notizen" -#: syntax.py:281 +#: syntax.py:291 msgid "journals" msgstr "Journal" -#: syntax.py:293 syntax.py:317 syntax.py:380 syntax.py:646 syntax.py:655 -#: syntax.py:665 +#: syntax.py:303 syntax.py:327 syntax.py:390 msgid "Value may not contain other than numbers, letters and dots!" msgstr "Der Wert darf nur Zahlen, Buchstaben und Punkte enthalten." -#: syntax.py:304 +#: syntax.py:314 msgid "Value may not contain other than numbers, letters, dots and spaces!" msgstr "" "Der Wert darf nur Zahlen, Buchstaben, Leerzeichen und Punkte enthalten." -#: syntax.py:326 +#: syntax.py:336 msgid "Field must only contain ASCII characters!" msgstr "Der Wert darf nur ASCII Buchstaben enthalten!" -#: syntax.py:339 +#: syntax.py:349 msgid "" "Value may not contain other than numbers, letters and dots, and may not be " "admin!" @@ -262,191 +261,194 @@ "Der Wert darf nur Zahlen, Buchstaben und Punkte enthalten. Ausnahme ist die " "Bezeichnug 'admin', die auch nicht verwendet werden darf" -#: syntax.py:351 syntax.py:368 +#: syntax.py:361 syntax.py:378 msgid "" "Username must only contain numbers, letters and dots, and may not be 'admin'!" msgstr "" "Der Wert darf nur Zahlen, Buchstaben und Punkte enthalten. Ausnahme ist die " "Bezeichnug 'admin', die auch nicht verwendet werden darf" -#: syntax.py:363 +#: syntax.py:373 msgid "Only the first letter of the username may be uppercase!" msgstr "Nur der erste Buchstabe des Benutzernames darf groß geschrieben sein!" -#: syntax.py:388 +#: syntax.py:398 msgid ", a path must begin with \"/\"!" msgstr ", eine Pfadangabe muss mit \\\"/\\\" beginnen." -#: syntax.py:392 +#: syntax.py:402 msgid "Value may not contain double quotes (\")!" msgstr "Der Wert darf kein Anführungszeichen (\") enthalten!" -#: syntax.py:406 +#: syntax.py:416 msgid "The password is to short, at least 8 characters needed." msgstr "Das Passwort ist zu kurz, mindestens 8 Zeichen sind erforderlich." -#: syntax.py:414 +#: syntax.py:424 msgid "Empty password not allowed!" msgstr "Ein leeres Passwort ist nicht erlaubt!" -#: syntax.py:427 +#: syntax.py:437 msgid "Not a valid hostname!" msgstr "Kein gültiger Hostname" -#: syntax.py:441 +#: syntax.py:451 msgid "Not a valid windows hostname!" msgstr "Kein gültiger Windows-Hostname" -#: syntax.py:453 syntax.py:456 +#: syntax.py:463 msgid "Not a valid IP address!" msgstr "Keine gültige IP-Adresse" -#: syntax.py:484 +#: syntax.py:491 msgid "Not a valid hostname or IP address!" msgstr "Kein gültiger Hostname oder IP-Adresse" -#: syntax.py:527 +#: syntax.py:534 msgid "Not a valid netmask!" msgstr "Keine gültige Netzwerkmaske." -#: syntax.py:531 +#: syntax.py:538 msgid "First Address" msgstr "Erste Adresse" -#: syntax.py:531 +#: syntax.py:538 msgid "Last Address" msgstr "Letzte Adresse" -#: syntax.py:542 +#: syntax.py:549 msgid "tcp" msgstr "" -#: syntax.py:542 +#: syntax.py:549 msgid "udp" msgstr "" -#: syntax.py:554 +#: syntax.py:561 msgid "Not an absolute path!" msgstr "Keine absolute Pfadangabe" -#: syntax.py:565 syntax.py:576 +#: syntax.py:572 syntax.py:583 msgid "Not a valid email address!" msgstr "Keine gültige E-Mail-Adresse" -#: syntax.py:588 +#: syntax.py:595 msgid "" "The given date does not confirm iso8601, example: \"2009-01-01T12:00:00+01:00" "\"." msgstr "" -#: syntax.py:606 +#: syntax.py:613 msgid "Not a valid Date" msgstr "Kein gültiges Datum" -#: syntax.py:620 +#: syntax.py:625 msgid "" -"An IP subnet consists of one to three numbers ranging from 0 to 255 " -"separated by dots." -msgstr "" -"Ein IP-Subnetz muss mit ein bis drei Zahlen zwischen 0 und 255 angegeben " -"werden, die mit einem Punkt getrennt werden." +"An IPv4 subnet consists of one to three numbers ranging from 0 to 255 " +"separated by dots, an IPv6 subnet consists of one to four groups of one to " +"four hexadecimal digits separated by colons (:)." +msgstr "Ein IPv4-Subnetz muss mit ein bis drei Zahlen zwischen 0 und 255 angegeben werden, die mit einem Punkt getrennt werden, ein IPv6-Subnetz muss mit ein bis vier Gruppen aus ein bis vier hexadezimalen Zeichen angegeben werden, die mit einem Doppelpunkt getrennt werden." -#: syntax.py:635 +#: syntax.py:637 msgid "" -"The name of a reverse zone consists of the reversed subnet address followed " -"by .in-addr.arpa. Example: \"0.168.192.in-addr.arpa\"" -msgstr "" -"Der Name einer Reverse Lookup Zone besteht aus dem inversen Subnetz, gefolgt " -"von .in-addr.arpa. z.B.: \"0.168.192.in-addr.arpa\"" +"The name of a reverse zone for IPv4 consists of the reversed subnet address " +"followed by .in-addr.arpa (example: \"0.168.192.in-addr.arpa\") or for IPv6 " +"in nibble format followed by .ip6.arpa (example: \"0.0.0.0.0.0.1.0.8.b." +"d.0.1.0.0.2.ip6.arpa\")" +msgstr "Der Name einer IPv4 Reverse Lookup Zone besteht aus dem inversen Subnetz, gefolgt von .in-addr.arpa (z.B.: \"0.168.192.in-addr.arpa\") oder für IPv6 im Nibble Format, gefolgt von .ip6.arpa (z.B. \"0.0.0.0.0.0.1.0.8.b.d.0.1.0.0.2.ip6.arpa\")." -#: syntax.py:643 +#: syntax.py:645 msgid "Missing value!" msgstr "Fehlender Wert!" -#: syntax.py:671 +#: syntax.py:648 syntax.py:657 syntax.py:667 +msgid "Value may not contain other than numbers, letters, dots and colons!" +msgstr "Der Wert darf nur Zahlen, Buchstaben, Leerzeichen, Punkte und Doppelpunkte enthalten." + +#: syntax.py:673 msgid "Key" msgstr "" -#: syntax.py:671 +#: syntax.py:673 msgid "Value" msgstr "" -#: syntax.py:676 syntax.py:686 +#: syntax.py:678 syntax.py:694 msgid "Priority" msgstr "Priorität" -#: syntax.py:676 +#: syntax.py:678 msgid "Mail Server" msgstr "Mail-Server" -#: syntax.py:681 +#: syntax.py:683 msgid "Service" msgstr "Dienst" -#: syntax.py:681 +#: syntax.py:683 msgid "Protocol" msgstr "Protokoll" -#: syntax.py:686 +#: syntax.py:689 +msgid "Street" +msgstr "Straße" + +#: syntax.py:689 +msgid "Postal code" +msgstr "Postleitzahl" + +#: syntax.py:689 +msgid "City" +msgstr "Stadt" + +#: syntax.py:694 msgid "Weight" msgstr "Gewichtung" -#: syntax.py:686 +#: syntax.py:694 msgid "Port" msgstr "Port" -#: syntax.py:686 +#: syntax.py:694 msgid "Server" msgstr "Server" -#: syntax.py:696 +#: syntax.py:704 msgid "Not a valid time format" msgstr "Keine gültige Zeitangabe" -#: syntax.py:696 -msgid "Street" -msgstr "Straße" - -#: syntax.py:696 -msgid "Postal code" -msgstr "Postleitzahl" - -#: syntax.py:696 -msgid "City" -msgstr "Stadt" - -#: syntax.py:709 +#: syntax.py:717 msgid "Not a valid time interval" msgstr "Kein gültiges Zeitintervall" -#: syntax.py:715 +#: syntax.py:723 msgid "Ethernet" msgstr "Ethernet" -#: syntax.py:715 +#: syntax.py:723 msgid "FDDI" msgstr "FDDI" -#: syntax.py:715 +#: syntax.py:723 msgid "Token-Ring" msgstr "Token-Ring" -#: syntax.py:724 syntax.py:740 +#: syntax.py:732 syntax.py:748 msgid "" "Value must have 6 two digit hexadecimal numbers separated by \"-\" or \":\" !" msgstr "" "Der Wert muss 6 mal 2 Hexadezimalwerte beinhalten, die mit einem \"-\" oder " "\":\"getrennt werden" -#: syntax.py:744 +#: syntax.py:752 msgid "Type" msgstr "Typ" -#: syntax.py:744 +#: syntax.py:752 msgid "Address" msgstr "Adresse" -#: syntax.py:757 +#: syntax.py:765 msgid "" "Value may not contain other than numbers, letters, underscore (\"_\") and " "minus (\"-\")!" @@ -454,35 +456,35 @@ "Der Wert darf nur Zahlen, Buchstaben, Unterstriche (\"_\") und Minuszeichen " "(\"-\") enthalten." -#: syntax.py:761 +#: syntax.py:769 msgid "Driver" msgstr "Treiber" -#: syntax.py:761 +#: syntax.py:769 msgid "Description" msgstr "Beschreibung" -#: syntax.py:785 syntax.py:850 +#: syntax.py:793 syntax.py:858 msgid "Not a valid LDAP DN" msgstr "Kein gültiger LDAP-DN" -#: syntax.py:816 +#: syntax.py:824 msgid "Invitation Policy" msgstr "Richtlinie für Einladungen" -#: syntax.py:823 +#: syntax.py:831 msgid "Shared folder user ACL" msgstr "Benutzer ACLs für gemeinsame Ordner" -#: syntax.py:828 syntax.py:839 +#: syntax.py:836 syntax.py:847 msgid "Not a valid shared folder ACL" msgstr "Keine gültige ACL für gemeinsame Ordner" -#: syntax.py:834 +#: syntax.py:842 msgid "Shared folder group ACL" msgstr "Gruppen ACLs für gemeinsame Ordner" -#: syntax.py:871 +#: syntax.py:879 msgid "" "Value consists of two integer numbers separated by an \"x\" (e.g. \"1024x768" "\")" @@ -490,312 +492,312 @@ "Der Wert muss zwei ganzzahlige Werte mit einem \"x\" dazwischen beinhalten " "(z.B. \"1024x768\")" -#: syntax.py:880 +#: syntax.py:888 msgid "" "Value consists of two integer numbers separated by a \"-\" (e.g. \"30-70\")" msgstr "" "Der Wert muss aus zwei durch ein \"-\" getrennte ganzzahlige Werte bestehen " "(z.B. \"30-70\")" -#: syntax.py:1116 +#: syntax.py:1203 msgid "User ID" msgstr "Benutzer-ID" -#: syntax.py:1121 +#: syntax.py:1208 msgid "Group ID" msgstr "Gruppen-ID" -#: syntax.py:1129 +#: syntax.py:1216 msgid "Windows Terminal Server Hosts" msgstr "Windows Terminalserver" -#: syntax.py:1134 +#: syntax.py:1221 msgid "Linux Terminal Server Hosts" msgstr "Linux Terminalserver" -#: syntax.py:1139 +#: syntax.py:1226 msgid "Authentication Server" msgstr "Authentisierungsserver" -#: syntax.py:1144 +#: syntax.py:1231 msgid "File Server" msgstr "Fileserver" -#: syntax.py:1161 syntax.py:1166 +#: syntax.py:1248 syntax.py:1253 msgid "Primary Group" msgstr "Primäre Gruppe" -#: syntax.py:1171 +#: syntax.py:1258 msgid "Network" msgstr "Netzwerk" -#: syntax.py:1177 syntax.py:1192 +#: syntax.py:1264 syntax.py:1279 msgid "DNS Entry" msgstr "DNS-Eintrag" -#: syntax.py:1182 syntax.py:1197 +#: syntax.py:1269 syntax.py:1284 msgid "DNS Entry Reverse" msgstr "Reverse Lookup DNS-Eintrag" -#: syntax.py:1187 syntax.py:1202 +#: syntax.py:1274 syntax.py:1289 msgid "DHCP Entry" msgstr "DHCP-Eintrag" -#: syntax.py:1207 +#: syntax.py:1294 msgid "DNS Entry Alias" msgstr "Alias DNS-Eintrag" -#: syntax.py:1199 +#: syntax.py:1305 #, python-format msgid "Entry does not have dnsEntryAlias Syntax: %s" msgstr "Eintrag hat nicht dnsEntryAlias Syntax: %s" -#: syntax.py:1204 +#: syntax.py:1310 msgid "Share" msgstr "Freigabe" -#: syntax.py:1599 syntax.py:1618 syntax.py:1632 syntax.py:1671 syntax.py:1760 +#: syntax.py:1702 syntax.py:1721 syntax.py:1735 syntax.py:1774 syntax.py:1863 msgid "all" msgstr "Alle" -#: syntax.py:1600 +#: syntax.py:1703 msgid "January" msgstr "Januar" -#: syntax.py:1601 +#: syntax.py:1704 msgid "February" msgstr "Februar" -#: syntax.py:1602 +#: syntax.py:1705 msgid "March" msgstr "März" -#: syntax.py:1603 +#: syntax.py:1706 msgid "April" msgstr "April" -#: syntax.py:1604 +#: syntax.py:1707 msgid "May" msgstr "Mai" -#: syntax.py:1605 +#: syntax.py:1708 msgid "June" msgstr "Juni" -#: syntax.py:1606 +#: syntax.py:1709 msgid "July" msgstr "Juli" -#: syntax.py:1607 +#: syntax.py:1710 msgid "August" msgstr "August" -#: syntax.py:1608 +#: syntax.py:1711 msgid "September" msgstr "September" -#: syntax.py:1609 +#: syntax.py:1712 msgid "October" msgstr "Oktober" -#: syntax.py:1610 +#: syntax.py:1713 msgid "November" msgstr "November" -#: syntax.py:1611 +#: syntax.py:1714 msgid "December" msgstr "Dezember" -#: syntax.py:1619 +#: syntax.py:1722 msgid "Monday" msgstr "Montag" -#: syntax.py:1620 +#: syntax.py:1723 msgid "Tuesday" msgstr "Dienstag" -#: syntax.py:1621 +#: syntax.py:1724 msgid "Wednesday" msgstr "Mittwoch" -#: syntax.py:1622 +#: syntax.py:1725 msgid "Thursday" msgstr "Donnerstag" -#: syntax.py:1623 +#: syntax.py:1726 msgid "Friday" msgstr "Freitag" -#: syntax.py:1624 +#: syntax.py:1727 msgid "Saturday" msgstr "Samstag" -#: syntax.py:1625 +#: syntax.py:1728 msgid "Sunday" msgstr "Sonntag" -#: syntax.py:1820 +#: syntax.py:1923 msgid "Domain Group" msgstr "Globale Gruppe" -#: syntax.py:1821 +#: syntax.py:1924 msgid "Local Group" msgstr "Lokale Gruppe" -#: syntax.py:1822 +#: syntax.py:1925 msgid "Well-Known Group" msgstr "Bekannte Gruppe" -#: syntax.py:1831 syntax.py:1838 syntax.py:1845 +#: syntax.py:1934 syntax.py:1941 syntax.py:1948 msgid "Soft limit" msgstr "Soft-Limit" -#: syntax.py:1831 syntax.py:1838 syntax.py:1845 +#: syntax.py:1934 syntax.py:1941 syntax.py:1948 msgid "Hard limit" msgstr "Hard-Limit" -#: syntax.py:1831 syntax.py:1845 +#: syntax.py:1934 syntax.py:1948 msgid "Group" msgstr "Gruppe" -#: syntax.py:1838 +#: syntax.py:1941 msgid "User" msgstr "Benutzer" -#: syntax.py:1861 +#: syntax.py:1964 msgid "synchronous" msgstr "synchron" -#: syntax.py:1862 +#: syntax.py:1965 msgid "asynchronous" msgstr "asynchron" -#: syntax.py:1873 +#: syntax.py:1976 #, python-format msgid "\"%s\" is not a Univention Admin Module." msgstr "\"%s\" ist kein Univention Admin Modul" -#: syntax.py:1911 +#: syntax.py:2014 msgid "None" msgstr "Keine" -#: syntax.py:1916 +#: syntax.py:2019 msgid "About" msgstr "Über" -#: syntax.py:1916 +#: syntax.py:2019 msgid "Browse" msgstr "Navigation" -#: syntax.py:1916 +#: syntax.py:2019 msgid "Wizards" msgstr "Assistenten" -#: syntax.py:1916 +#: syntax.py:2019 msgid "Personal Settings" msgstr "Persönliche Einstellungen" -#: syntax.py:1930 +#: syntax.py:2033 msgid "No Reboot" msgstr "Nicht neu starten" -#: syntax.py:1931 +#: syntax.py:2034 msgid "Immediately" msgstr "Sofort" -#: syntax.py:1937 +#: syntax.py:2040 msgid "Groupware Account" msgstr "Groupware Konto" -#: syntax.py:1938 +#: syntax.py:2041 msgid "Kerberos Principal" msgstr "Kerberos Prinzipal" -#: syntax.py:1939 +#: syntax.py:2042 msgid "Personal Information" msgstr "Persönliche Information" -#: syntax.py:1940 +#: syntax.py:2043 msgid "Samba Account" msgstr "Samba Konto" -#: syntax.py:1941 +#: syntax.py:2044 msgid "Posix Account" msgstr "Posix Konto" -#: syntax.py:1942 +#: syntax.py:2045 msgid "Mail Account" msgstr "Mail Konto" -#: syntax.py:1950 +#: syntax.py:2053 msgid "Disconnect" msgstr "Trennen" -#: syntax.py:1951 +#: syntax.py:2054 msgid "Reset" msgstr "Zurücksetzen" -#: syntax.py:1959 +#: syntax.py:2062 msgid "All Clients" msgstr "Von jedem Client" -#: syntax.py:1960 +#: syntax.py:2063 msgid "Previously used Client" msgstr "Nur von vorherigem Client" -#: syntax.py:1968 syntax.py:1979 +#: syntax.py:2071 syntax.py:2082 msgid "Disabled" msgstr "Deaktiviert" -#: syntax.py:1969 +#: syntax.py:2072 msgid "Enabled: Input: on, Message: on" msgstr "Aktiviert: Eingabe: ein, Benachrichtigen: ein" -#: syntax.py:1970 +#: syntax.py:2073 msgid "Enabled: Input: on, Message: off" msgstr "Aktiviert: Eingabe: an, Benachrichtigen: aus" -#: syntax.py:1971 +#: syntax.py:2074 msgid "Enabled: Input: off, Message: on" msgstr "Aktiviert: Eingabe: aus, Benachrichtigen: an" -#: syntax.py:1972 +#: syntax.py:2075 msgid "Enabled: Input: off, Message: off" msgstr "Aktiviert: Eingabe: aus, Benachrichtigen: aus" -#: syntax.py:1980 +#: syntax.py:2083 msgid "Enabled: Set by Caller" msgstr "" -#: syntax.py:1981 +#: syntax.py:2084 msgid "Enabled: No Call Back" msgstr "" -#: syntax.py:2064 +#: syntax.py:2172 msgid "NFS-Share" msgstr "NFS-Freigabe" -#: syntax.py:2077 +#: syntax.py:2185 msgid "Language code must be in format \"xx_XX\"!" msgstr "Sprach-Code muss im Format \"xx_XX\" angegeben werden!" -#: syntax.py:2083 syntax.py:2087 syntax.py:2090 syntax.py:2093 +#: syntax.py:2191 syntax.py:2195 syntax.py:2198 syntax.py:2201 msgid "Language code (e.g. en_US)" msgstr "Sprachcode (z.B. de_DE)" -#: syntax.py:2083 +#: syntax.py:2191 msgid "Text" msgstr "Text" -#: syntax.py:2087 +#: syntax.py:2195 msgid "Translated short description" msgstr "Übersetzte Kurzbeschreibung" -#: syntax.py:2090 +#: syntax.py:2198 msgid "Translated long description" msgstr "Übersetzte Langbeschreibung" -#: syntax.py:2093 +#: syntax.py:2201 msgid "Translated tab name" msgstr "Übersetzter Karteikartenname" @@ -880,8 +882,8 @@ "The DNS alias entry for this host should contain the zone name, the alias " "zone container DN and the alias." msgstr "" -"Der DNS-Alias Eintrag für diesen Rechner sollte den Zonennamen, die LDAP-DN des Alias-Zonencontainers und " -"den Alias-Namen enthalten." +"Der DNS-Alias Eintrag für diesen Rechner sollte den Zonennamen, die LDAP-DN " +"des Alias-Zonencontainers und den Alias-Namen enthalten." #: uexceptions.py:102 msgid "Next IP address not found." @@ -1075,7 +1077,7 @@ "Zum Zuweisen von Nagios-Diensten wird ein Eintrag in der DNS Forward Zone " "benötigt!" -#: uexceptions.py:228 +#: uexceptions.py:234 msgid "" "The DNS forward entry could not be created. Please remove existing alias " "records or comparable DNS objects with the same name as this host from the " @@ -1085,7 +1087,7 @@ "DNS-Alias Records oder vergleichbare DNS-Objekte mit dem Namen dieses " "Rechners aus der DNS-Forward-Zone entfernt werden." -#: uexceptions.py:231 +#: uexceptions.py:237 msgid "Circular group dependency detected: " msgstr "Zyklische Gruppen-Abhängigkeit festgestellt: " @@ -1102,7 +1104,7 @@ msgid "Authentication failed" msgstr "Authentisierung fehlgeschlagen" -#: uldap.py:357 +#: uldap.py:361 msgid "Moving childs is not supported." msgstr "Das Verschieben von Kindsknoten wird nicht unterstützt." Index: modules/univention/admin/syntax.py =================================================================== --- modules/univention/admin/syntax.py (Revision 13639) +++ modules/univention/admin/syntax.py (Arbeitskopie) @@ -29,6 +29,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re, string, types, math, time, operator +import ipaddr # IPv6 address handling import univention.debug import univention.admin.modules import univention.admin.uexceptions @@ -451,18 +452,16 @@ class ipAddress(simple): name='ipAddress' - min_length=7 - max_length=15 - _re = re.compile('^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$') - + min_length=2 + max_length=39 + # match IPv4 (0.0.0.0 is allowed) or IPv6 address (with IPv4-mapped IPv6) def parse(self, text): - if text and self._re.match(text) != None: - for q in text.split('.'): - if int(q) > 255: - raise univention.admin.uexceptions.valueError, _("Not a valid IP address!") - return + if text != None: + try: + ipaddr.IPAddress(text) + except ValueError: + raise univention.admin.uexceptions.valueError, _("Not a valid IP address!") return text - raise univention.admin.uexceptions.valueError, _("Not a valid IP address!") class hostOrIP(simple): name='host' @@ -470,14 +469,13 @@ max_length=0 def ipAddress(self, text): - _re = re.compile('(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$)') - if text and _re.match(text) != None: - for q in text.split('.'): - if int(q) > 255: - return False + # match IPv4 (0.0.0.0 is allowed) or IPv6 address (incl. IPv4-mapped IPv6) + if text != None: + try: + ipaddr.IPAddress(text) + except ValueError: + return False return True - else: - return False def hostName(self, text): _re = re.compile("(^[a-zA-Z])(([a-zA-Z0-9-]*)([a-zA-Z0-9]$))?$") @@ -491,7 +489,7 @@ return text else: raise univention.admin.uexceptions.valueError, _('Not a valid hostname or IP address!') - +# TODO: IPv6 class netmask(simple): name='netmask' min_length=1 @@ -617,61 +615,56 @@ class reverseLookupSubnet(simple): name='reverseLookupSubnet' min_length=1 - max_length=15 - _re = re.compile('^[0-9]+(\.[0-9]+)?(\.[0-9]+)?$') + max_length=19 + _re = re.compile('^((0|[3-9][0-9]?|2(?:5[0-5]|[0-4]?[0-9])?|1[0-9]{0,2})(\.(0|[3-9][0-9]?|2(?:5[0-5]|[0-4]?[0-9])?|1[0-9]{0,2})){0,2})$|^([0-9a-fA\ +-F]{1,4}(?:(?:(? 255: - return return text - raise univention.admin.uexceptions.valueError,_("An IP subnet consists of one to three numbers ranging from 0 to 255 separated by dots.") + raise univention.admin.uexceptions.valueError,_("An IPv4 subnet consists of one to three numbers ranging from 0 to 255 separated by dots, an IPv6 subnet consists of one to four groups of one to four hexadecimal digits separated by colons (:).") class reverseLookupZoneName(simple): name='reverseLookupZoneName' min_length=14 - max_length=30 #? - _re=re.compile('^[0-9]+(\.[0-9]+)?(\.[0-9]+)?\.in-addr\.arpa$') + max_length=72 # IPv6 + # TODO: check re.compile IPv6 + _re = re.compile('^((0|[3-9][0-9]?|2(?:5[0-5]|[0-4]?[0-9])?|1[0-9]{0,2})(\.(0|[3-9][0-9]?|2(?:5[0-5]|[0-4]?[0-9])?|1[0-9]{0,2})){3}\.in-addr\.arpa$)|((?!.*?::.*?::)[0-9a-fA-F]{0,4}(?:(?:(? 255: - return return text - raise univention.admin.uexceptions.valueError,_("The name of a reverse zone consists of the reversed subnet address followed by .in-addr.arpa. Example: \"0.168.192.in-addr.arpa\"") + raise univention.admin.uexceptions.valueError,_("The name of a reverse zone for IPv4 consists of the reversed subnet address followed by .in-addr.arpa (example: \"0.168.192.in-addr.arpa\") or for IPv6 in nibble format followed by .ip6.arpa (example: \"0.0.0.0.0.0.1.0.8.b.d.0.1.0.0.2.ip6.arpa\")") class dnsName(simple): name='dnsName' - _re = re.compile('^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9.]$') + _re = re.compile('^[a-zA-Z0-9:][a-zA-Z0-9.:_-]*[a-zA-Z0-9.]$') # IPv6 def parse(self, text): if text==None: raise univention.admin.uexceptions.valueError, _("Missing value!") if self._re.match(text) != None: return text - raise univention.admin.uexceptions.valueError, _("Value may not contain other than numbers, letters and dots!") + raise univention.admin.uexceptions.valueError, _("Value may not contain other than numbers, letters, dots and colons!") class dnsName_umlauts(simple): name='dnsName_umlauts' - _re = re.compile('(?u)(^\w[\w -.]*\w$)|\w*$') + _re = re.compile('(?u)(^\w[\w -.:]*\w$)|\w*$') # IPv6 def parse(self, text): if self._re.match(text) != None: return text - raise univention.admin.uexceptions.valueError, _("Value may not contain other than numbers, letters and dots!") + raise univention.admin.uexceptions.valueError, _("Value may not contain other than numbers, letters, dots and colons!") class dnsNameDot(simple): name='dnsName' - _re = re.compile('^[0-9a-zA-Z.-]+$') + _re = re.compile('^[0-9a-zA-Z.:-]+$') # IPv6 def parse(self, text): if self._re.match(text) != None: return text - raise univention.admin.uexceptions.valueError, _("Value may not contain other than numbers, letters and dots!") + raise univention.admin.uexceptions.valueError, _("Value may not contain other than numbers, letters, dots and colons!") class keyAndValue(complex):