Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
  • N noc
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 427
    • Issues 427
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
    • Requirements
  • Merge requests 17
    • Merge requests 17
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Package Registry
    • Container Registry
    • Infrastructure Registry
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • External wiki
    • External wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • noc
  • noc
  • Issues
  • #1586

Closed
Open
Created May 12, 2021 by nielsdenotter@nielsdenotter

Stacktrace generation reverse IPv6 DNS zone with hex characters in begin of PTR

Steps to reproduce

  1. The generation of an IPv6 DNS Reverse Zone fails in occasions where non-digit characters are compared during sort of the zonefile.

  2. This generates a stack trace.

  3. Easily reproducible. Certainly when using non-digit hexadecimal characters at the end of an IPv6 address.

What is the current bug behavior?

Python stack trace when the generation of the IPv6 RR zone fails. No update of the reverse zone in the DNS-zones view.

What is the expected correct behavior?

A correct and updated IPv6 reverse DNS zone.

Relevant logs and/or screenshots

2021-05-12 10:05:02,592 [noc.core.debug] UNHANDLED EXCEPTION (2021-05-12 10:05:02.557324)
PROCESS: ./commands/datastream.py
VERSION: 20.4.3
BRANCH: HEAD CHANGESET: c86766dc
ERROR FINGERPRINT: 242fa573-9da4-5260-bc1f-a75563224ff5
WORKING DIRECTORY: /opt/noc
EXCEPTION: <class 'TypeError'> '<' not supported between instances of 'int' and 'str'
START OF TRACEBACK
------------------------------------------------------------------------
File: core/dns/rr.py (Line: 62)
Function: __lt__
   55             # Check type preferences
   56             p1 = TYPE_PREF.get(self.type, DEFAULT_PREF)
   57             p2 = TYPE_PREF.get(other.type, DEFAULT_PREF)
   58             if p1 != p2:
   59                 return p1 < p2
   60             # Compare PTR
   61             if self.type == "PTR" and other.type == "PTR":
   62 ==>             return self._order < other._order
   63             # Compare by name
   64             if self._sorder < other._sorder:
   65                 return True
   66             elif self._sorder > other._sorder:
   67                 return False
   68             # Compare by type
Variables:
                self = 
<RR 0.6.1.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.0.0 PTR ae21.bkl001a-jnx-01.bugreport.net.>
               other = 
<RR f.3.1.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.0.0 PTR ae25.bd011a-jnx-01.bugreport.net.>
                  p1 = 100
                  p2 = 100
------------------------------------------------------------------------
File: services/datastream/streams/dnszone.py (Line: 60)
Function: get_records
   53             """
   54             zone_iters = [cls.iter_soa(zone)]
   55             if zone.type == ZONE_FORWARD:
   56                 zone_iters += [sorted(cls.iter_forward(zone))]
   57             elif zone.type == ZONE_REVERSE_IPV4:
   58                 zone_iters += [sorted(cls.iter_reverse_ipv4(zone))]
   59             elif zone.type == ZONE_REVERSE_IPV6:
   60 ==>             zone_iters += [sorted(cls.iter_reverse_ipv6(zone))]
   61             return [x.to_json() for x in chain(*tuple(zone_iters))]
   62     
   63         @classmethod
   64         def iter_soa(cls, zone):
   65             """
   66             Yield SOA record
Variables:
                 cls = <class 'noc.services.datastream.streams.dnszone.DNSZoneDataStream'>
                zone = <DNSZone: e.0.0.1.6.0.1.0.0.2.ip6.arpa>
          zone_iters = [<generator object DNSZoneDataStream.iter_soa at 0x7f5d04d96c80>]
------------------------------------------------------------------------
File: services/datastream/streams/dnszone.py (Line: 44)
Function: get_object
   37             zone = zone[0]
   38             return {
   39                 "id": str(zone.id),
   40                 "name": str(zone.name),
   41                 "serial": str(zone.serial),
   42                 "masters": [str(x[:-1]) for x in zone.masters],
   43                 "slaves": [str(x[:-1]) for x in zone.slaves],
   44 ==>             "records": cls.get_records(zone),
   45             }
   46     
   47         @classmethod
   48         def get_records(cls, zone):
   49             """
   50             Get zone records
Variables:
                 cls = <class 'noc.services.datastream.streams.dnszone.DNSZoneDataStream'>
                  id = 45
                zone = <DNSZone: e.0.0.1.6.0.1.0.0.2.ip6.arpa>
------------------------------------------------------------------------
File: core/datastream/base.py (Line: 234)
Function: _get_current_data
  227         @classmethod
  228         def _get_current_data(
  229             cls, obj_id, delete=False
  230         ) -> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]:
  231             if delete:
  232                 return cls.get_deleted_object(obj_id), None
  233             try:
  234 ==>             data = cls.get_object(obj_id)
  235                 meta = cls.get_meta(data)
  236                 return data, meta
  237             except KeyError:
  238                 return cls.get_deleted_object(obj_id), None
  239     
  240         @classmethod
Variables:
                 cls = <class 'noc.services.datastream.streams.dnszone.DNSZoneDataStream'>
              obj_id = 45
              delete = False
------------------------------------------------------------------------
File: core/datastream/base.py (Line: 149)
Function: bulk_update
  142                     for doc in coll.find({cls.F_ID: {"$in": chunk}}, {cls.F_ID: 1, cls.F_HASH: 1})
  143                 }
  144                 bulk = []
  145                 fmt_data = defaultdict(list)
  146                 fmt_bulk = {}
  147                 # Apply default format
  148                 for obj_id in chunk:
  149 ==>                 data, meta = cls._get_current_data(obj_id)
  150                     cls._update_object(data=data, meta=meta, state=current_state, bulk=bulk)
  151                     # Process formats
  152                     for fmt in fmt_handler:
  153                         fmt_data[fmt] += list(fmt_handler[fmt](data))
  154                 # Apply formats
  155                 for fmt in fmt_data:

Possible fixes

Problem appears to be in core/dns/rr.py and more specifically;

        if self.type == "PTR" and other.type == "PTR":
            return self._order < other._order

Here the tuples of ._order are compared from left to right. However earlier on the _order type was casted int when it was an integer, but stays string for non-integers.

        if type == "PTR":
            self._order = tuple(self.maybe_int(x) for x in name.split("."))

This imo causes the stacktrace above when the comparision first meets a hexadecimal character instead of a digit to be compared.

I tried this possible fix to ensure this was the problem;

    def maybe_int(v):
        try:
            return int(v)
        except ValueError:
            return int(v,base=16)

Here a base16 int is returned instead of 'v' as string. Doing so fixes the problem in our situation. If this is safe enough for all environments I cannot decide. But since this is only for reverse zones, it appears safe to me. Otherwise the code needs if-then-else based on the zone-type.

Paste NOC version (./noc about)

NOC version is: 20.4.3

Edited May 12, 2021 by nielsdenotter
Assignee
Assign to
Time tracking