dissect.database 1.1.dev10__tar.gz → 1.1.dev11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. {dissect_database-1.1.dev10/dissect.database.egg-info → dissect_database-1.1.dev11}/PKG-INFO +1 -1
  2. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/index.py +1 -1
  3. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/database.py +16 -2
  4. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/object.py +10 -5
  5. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/query.py +4 -5
  6. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/schema.py +200 -52
  7. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/util.py +48 -113
  8. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11/dissect.database.egg-info}/PKG-INFO +1 -1
  9. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_util.py +6 -3
  10. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/COPYRIGHT +0 -0
  11. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/LICENSE +0 -0
  12. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/MANIFEST.in +0 -0
  13. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/README.md +0 -0
  14. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/__init__.py +0 -0
  15. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/__init__.py +0 -0
  16. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/c_db.py +0 -0
  17. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/c_db.pyi +0 -0
  18. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/db.py +0 -0
  19. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/tools/__init__.py +0 -0
  20. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/tools/c_rpm.py +0 -0
  21. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/tools/c_rpm.pyi +0 -0
  22. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/bsd/tools/rpm.py +0 -0
  23. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/__init__.py +0 -0
  24. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/btree.py +0 -0
  25. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/c_ese.py +0 -0
  26. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/c_ese.pyi +0 -0
  27. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/compression.py +0 -0
  28. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/cursor.py +0 -0
  29. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ese.py +0 -0
  30. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/exception.py +0 -0
  31. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/lcmapstring.py +0 -0
  32. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/__init__.py +0 -0
  33. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/c_ds.py +0 -0
  34. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/c_ds.pyi +0 -0
  35. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/c_pek.py +0 -0
  36. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/c_pek.pyi +0 -0
  37. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/c_sd.py +0 -0
  38. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/c_sd.pyi +0 -0
  39. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/ntds.py +0 -0
  40. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/__init__.py +0 -0
  41. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/applicationsettings.py +0 -0
  42. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/attributeschema.py +0 -0
  43. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/builtindomain.py +0 -0
  44. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/certificationauthority.py +0 -0
  45. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/classschema.py +0 -0
  46. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/classstore.py +0 -0
  47. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/computer.py +0 -0
  48. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/configuration.py +0 -0
  49. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/container.py +0 -0
  50. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/controlaccessright.py +0 -0
  51. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/crldistributionpoint.py +0 -0
  52. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/crossref.py +0 -0
  53. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/crossrefcontainer.py +0 -0
  54. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/dfsconfiguration.py +0 -0
  55. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/displayspecifier.py +0 -0
  56. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/dmd.py +0 -0
  57. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/dnsnode.py +0 -0
  58. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/dnszone.py +0 -0
  59. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/domain.py +0 -0
  60. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/domaindns.py +0 -0
  61. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/domainpolicy.py +0 -0
  62. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/dsuisettings.py +0 -0
  63. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/filelinktracking.py +0 -0
  64. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/foreignsecurityprincipal.py +0 -0
  65. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/group.py +0 -0
  66. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/grouppolicycontainer.py +0 -0
  67. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/infrastructureupdate.py +0 -0
  68. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/intersitetransport.py +0 -0
  69. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/intersitetransportcontainer.py +0 -0
  70. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ipsecbase.py +0 -0
  71. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ipsecfilter.py +0 -0
  72. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ipsecisakmppolicy.py +0 -0
  73. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ipsecnegotiationpolicy.py +0 -0
  74. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ipsecnfa.py +0 -0
  75. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ipsecpolicy.py +0 -0
  76. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/leaf.py +0 -0
  77. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/linktrackobjectmovetable.py +0 -0
  78. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/linktrackvolumetable.py +0 -0
  79. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/locality.py +0 -0
  80. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/lostandfound.py +0 -0
  81. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msauthz_centralaccesspolicies.py +0 -0
  82. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msauthz_centralaccessrules.py +0 -0
  83. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_content.py +0 -0
  84. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_contentset.py +0 -0
  85. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_globalsettings.py +0 -0
  86. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_localsettings.py +0 -0
  87. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_member.py +0 -0
  88. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_replicationgroup.py +0 -0
  89. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_subscriber.py +0 -0
  90. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_subscription.py +0 -0
  91. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdfsr_topology.py +0 -0
  92. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msdns_serversettings.py +0 -0
  93. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_authnpolicies.py +0 -0
  94. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_authnpolicysilos.py +0 -0
  95. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_claimstransformationpolicies.py +0 -0
  96. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_claimtype.py +0 -0
  97. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_claimtypepropertybase.py +0 -0
  98. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_claimtypes.py +0 -0
  99. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_optionalfeature.py +0 -0
  100. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_passwordsettingscontainer.py +0 -0
  101. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_quotacontainer.py +0 -0
  102. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_resourceproperties.py +0 -0
  103. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_resourceproperty.py +0 -0
  104. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_resourcepropertylist.py +0 -0
  105. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_shadowprincipalcontainer.py +0 -0
  106. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msds_valuetype.py +0 -0
  107. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msimaging_psps.py +0 -0
  108. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/mskds_provserverconfiguration.py +0 -0
  109. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msmqenterprisesettings.py +0 -0
  110. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/mspki_enterpriseoid.py +0 -0
  111. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/mspki_privatekeyrecoveryagent.py +0 -0
  112. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/msspp_activationobjectscontainer.py +0 -0
  113. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/mstpm_informationobjectscontainer.py +0 -0
  114. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ntdsconnection.py +0 -0
  115. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ntdsdsa.py +0 -0
  116. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ntdsservice.py +0 -0
  117. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ntdssitesettings.py +0 -0
  118. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ntfrssettings.py +0 -0
  119. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/organizationalperson.py +0 -0
  120. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/organizationalunit.py +0 -0
  121. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/person.py +0 -0
  122. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/physicallocation.py +0 -0
  123. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/pkicertificatetemplate.py +0 -0
  124. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/pkienrollmentservice.py +0 -0
  125. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/querypolicy.py +0 -0
  126. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ridmanager.py +0 -0
  127. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/ridset.py +0 -0
  128. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/rpccontainer.py +0 -0
  129. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/rrasadministrationdictionary.py +0 -0
  130. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/samserver.py +0 -0
  131. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/secret.py +0 -0
  132. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/securityobject.py +0 -0
  133. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/server.py +0 -0
  134. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/serverscontainer.py +0 -0
  135. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/site.py +0 -0
  136. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/sitelink.py +0 -0
  137. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/sitescontainer.py +0 -0
  138. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/subnetcontainer.py +0 -0
  139. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/subschema.py +0 -0
  140. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/top.py +0 -0
  141. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/trusteddomain.py +0 -0
  142. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/objects/user.py +0 -0
  143. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/pek.py +0 -0
  144. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/ntds/sd.py +0 -0
  145. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/page.py +0 -0
  146. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/record.py +0 -0
  147. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/sorting_table.py +0 -0
  148. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/table.py +0 -0
  149. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/tools/__init__.py +0 -0
  150. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/tools/certlog.py +0 -0
  151. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/tools/impacket.py +0 -0
  152. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/tools/sru.py +0 -0
  153. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/tools/ual.py +0 -0
  154. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/ese/util.py +0 -0
  155. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/exception.py +0 -0
  156. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/__init__.py +0 -0
  157. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/c_sqlite3.py +0 -0
  158. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/c_sqlite3.pyi +0 -0
  159. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/encryption/__init__.py +0 -0
  160. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/encryption/sqlcipher/__init__.py +0 -0
  161. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/encryption/sqlcipher/exception.py +0 -0
  162. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py +0 -0
  163. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/exception.py +0 -0
  164. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/sqlite3.py +0 -0
  165. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/util.py +0 -0
  166. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect/database/sqlite3/wal.py +0 -0
  167. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect.database.egg-info/SOURCES.txt +0 -0
  168. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect.database.egg-info/dependency_links.txt +0 -0
  169. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect.database.egg-info/requires.txt +0 -0
  170. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/dissect.database.egg-info/top_level.txt +0 -0
  171. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/pyproject.toml +0 -0
  172. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/setup.cfg +0 -0
  173. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/__init__.py +0 -0
  174. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/_docs/Makefile +0 -0
  175. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/_docs/conf.py +0 -0
  176. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/_docs/index.rst +0 -0
  177. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/_tools/sqlite3/__init__.py +0 -0
  178. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/_tools/sqlite3/generate_sqlite.py +0 -0
  179. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/_util.py +0 -0
  180. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/bsd/__init__.py +0 -0
  181. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/bsd/conftest.py +0 -0
  182. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/bsd/test_db.py +0 -0
  183. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/bsd/test_rpm.py +0 -0
  184. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/conftest.py +0 -0
  185. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/__init__.py +0 -0
  186. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/conftest.py +0 -0
  187. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/__init__.py +0 -0
  188. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/conftest.py +0 -0
  189. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_benchmark.py +0 -0
  190. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_ntds.py +0 -0
  191. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_pek.py +0 -0
  192. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_query.py +0 -0
  193. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_schema.py +0 -0
  194. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/ntds/test_sd.py +0 -0
  195. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/test_cursor.py +0 -0
  196. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/test_ese.py +0 -0
  197. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/test_index.py +0 -0
  198. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/test_page.py +0 -0
  199. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/test_record.py +0 -0
  200. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/test_table.py +0 -0
  201. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/tools/__init__.py +0 -0
  202. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/tools/test_certlog.py +0 -0
  203. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/tools/test_sru.py +0 -0
  204. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/ese/tools/test_ual.py +0 -0
  205. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/__init__.py +0 -0
  206. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/conftest.py +0 -0
  207. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/test_default_values.py +0 -0
  208. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/test_row.py +0 -0
  209. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/test_sqlcipher.py +0 -0
  210. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/test_sqlite3.py +0 -0
  211. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/test_util.py +0 -0
  212. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tests/sqlite3/test_wal.py +0 -0
  213. {dissect_database-1.1.dev10 → dissect_database-1.1.dev11}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.database
3
- Version: 1.1.dev10
3
+ Version: 1.1.dev11
4
4
  Summary: A Dissect module implementing parsers for various database formats, including Berkeley DB, Microsofts Extensible Storage Engine (ESE) and SQLite3
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License-Expression: Apache-2.0
@@ -282,7 +282,7 @@ def _encode_text(index: Index, column: Column, value: str, max_size: int) -> byt
282
282
  else:
283
283
  # Unicode strings == LCMapStringW
284
284
  flags = index.record.get("LCMapFlags")
285
- locale = index.record.get("LocaleName").decode("utf-16-le")
285
+ locale = local_name.decode("utf-16-le") if (local_name := index.record.get("LocaleName")) is not None else ""
286
286
  segment = map_string(value, flags, locale)
287
287
  key += segment[:max_size]
288
288
 
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  from collections.abc import Iterator
18
18
 
19
19
  from dissect.database.ese.index import Index
20
- from dissect.database.ese.ntds.objects import Top
20
+ from dissect.database.ese.ntds.objects import DMD, NTDSDSA, Top
21
21
 
22
22
 
23
23
  class Database:
@@ -43,6 +43,8 @@ class DataTable:
43
43
  def __init__(self, db: Database):
44
44
  self.db = db
45
45
  self.table = self.db.ese.table("datatable")
46
+ self.hiddentable = self.db.ese.table("hiddentable")
47
+ self.hiddeninfo = next(self.hiddentable.records(), None)
46
48
 
47
49
  self.schema = Schema()
48
50
 
@@ -50,6 +52,18 @@ class DataTable:
50
52
  self.get = lru_cache(4096)(self.get)
51
53
  self._make_dn = lru_cache(4096)(self._make_dn)
52
54
 
55
+ def dsa(self) -> NTDSDSA:
56
+ """Return the Directory System Agent (DSA) object."""
57
+ if not self.hiddeninfo:
58
+ raise ValueError("No hiddentable information available")
59
+ return self.get(self.hiddeninfo.get("dsa_col"))
60
+
61
+ def dmd(self) -> DMD:
62
+ """Return the Directory Management Domain (DMD) object, a.k.a. the schema container."""
63
+ if not self.hiddeninfo:
64
+ raise ValueError("No hiddentable information available")
65
+ return self.get(self.dsa().get("dMDLocation", raw=True))
66
+
53
67
  def root(self) -> Top:
54
68
  """Return the top-level object in the NTDS database."""
55
69
  if (root := next(self.children_of(0), None)) is None:
@@ -129,7 +143,7 @@ class DataTable:
129
143
  raise ValueError(f"Attribute {key!r} is not found in the schema")
130
144
 
131
145
  index = self.table.find_index(schema.column)
132
- record = index.search([encode_value(self.db, key, value)])
146
+ record = index.search([encode_value(self.db, schema, value)])
133
147
  return Object.from_record(self.db, record)
134
148
 
135
149
  def query(self, query: str, *, optimize: bool = True) -> Iterator[Object]:
@@ -64,10 +64,15 @@ class Object:
64
64
  db: The database instance associated with this object.
65
65
  record: The :class:`Record` instance representing this object.
66
66
  """
67
- if (object_classes := _get_attribute(db, record, "objectClass")) and (
68
- known_cls := cls.__known_classes__.get(object_classes[0])
69
- ) is not None:
70
- return known_cls(db, record)
67
+ try:
68
+ if (object_classes := _get_attribute(db, record, "objectClass")) and (
69
+ known_cls := cls.__known_classes__.get(object_classes[0])
70
+ ) is not None:
71
+ return known_cls(db, record)
72
+ except ValueError:
73
+ # Resolving the objectClass values can fail if the schema is not loaded yet (or is malformed)
74
+ # Fallback to a generic Object in that case
75
+ pass
71
76
 
72
77
  return cls(db, record)
73
78
 
@@ -268,4 +273,4 @@ def _get_attribute(db: Database, record: Record, name: str, *, raw: bool = False
268
273
  if raw:
269
274
  return value
270
275
 
271
- return decode_value(db, name, value)
276
+ return decode_value(db, schema, value)
@@ -83,7 +83,7 @@ class Query:
83
83
  raise NotImplementedError("Wildcards in the middle or start of the value are not yet supported")
84
84
  else:
85
85
  # Exact match query
86
- encoded_value = encode_value(self.db, filter.attribute, filter.value)
86
+ encoded_value = encode_value(self.db, schema, filter.value)
87
87
  yield from index.cursor().find_all(**{schema.column: encoded_value})
88
88
 
89
89
  def _process_and_operation(self, filter: SearchFilter, records: list[Record] | None) -> Iterator[Record]:
@@ -132,12 +132,11 @@ class Query:
132
132
  Yields:
133
133
  Records that match the filter criteria.
134
134
  """
135
- encoded_value = encode_value(self.db, filter.attribute, filter.value)
136
- schema = self.db.data.schema.lookup_attribute(name=filter.attribute)
137
-
138
- if schema is None:
135
+ if (schema := self.db.data.schema.lookup_attribute(name=filter.attribute)) is None:
139
136
  return
140
137
 
138
+ encoded_value = encode_value(self.db, schema, filter.value)
139
+
141
140
  has_wildcard = "*" in filter.value
142
141
  wildcard_prefix = filter.value.replace("*", "").lower() if has_wildcard else None
143
142
 
@@ -1,13 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from io import BytesIO
3
4
  from typing import TYPE_CHECKING, NamedTuple
4
5
 
5
- from dissect.database.ese.ntds.objects.object import Object
6
- from dissect.database.ese.ntds.util import OID_TO_TYPE, attrtyp_to_oid
6
+ from dissect.database.ese.ntds.c_ds import c_ds
7
7
 
8
8
  if TYPE_CHECKING:
9
- from collections.abc import Iterator
10
-
11
9
  from dissect.database.ese.ntds.database import Database
12
10
  from dissect.database.ese.ntds.util import SearchFlags
13
11
 
@@ -50,6 +48,8 @@ BOOTSTRAP_ATTRIBUTES = [
50
48
  ("searchFlags", 131406, 0x00080009, True), # ATTj131406
51
49
  # Class schema
52
50
  ("governsID", 131094, 0x00080002, True), # ATTc131094
51
+ # DSA attributes
52
+ ("dMDLocation", 131108, 0x00080001, True), # ATTb131108
53
53
  ]
54
54
 
55
55
  # For convenience, bootstrap some common object classes
@@ -60,21 +60,63 @@ BOOTSTRAP_OBJECT_CLASSES = {
60
60
  "attributeSchema": 0x0003000E,
61
61
  }
62
62
 
63
+ # These are fixed OID prefixes used in the schema
64
+ # Reference: MSPrefixTable
65
+ BOOTSTRAP_OID_PREFIXES = {
66
+ 0: b"\x55\x04",
67
+ 1: b"\x55\x06",
68
+ 2: b"\x2a\x86\x48\x86\xf7\x14\x01\x02",
69
+ 3: b"\x2a\x86\x48\x86\xf7\x14\x01\x03",
70
+ 4: b"\x60\x86\x48\x01\x65\x02\x02\x01",
71
+ 5: b"\x60\x86\x48\x01\x65\x02\x02\x03",
72
+ 6: b"\x60\x86\x48\x01\x65\x02\x01\x05",
73
+ 7: b"\x60\x86\x48\x01\x65\x02\x01\x04",
74
+ 8: b"\x55\x05",
75
+ 9: b"\x2a\x86\x48\x86\xf7\x14\x01\x04",
76
+ 10: b"\x2a\x86\x48\x86\xf7\x14\x01\x05",
77
+ 11: b"\x2a\x86\x48\x86\xf7\x14\x01\x04\x82\x04",
78
+ 12: b"\x2a\x86\x48\x86\xf7\x14\x01\x05\x38",
79
+ 13: b"\x2a\x86\x48\x86\xf7\x14\x01\x04\x82\x06",
80
+ 14: b"\x2a\x86\x48\x86\xf7\x14\x01\x05\x39",
81
+ 15: b"\x2a\x86\x48\x86\xf7\x14\x01\x04\x82\x07",
82
+ 16: b"\x2a\x86\x48\x86\xf7\x14\x01\x05\x3a",
83
+ 17: b"\x2a\x86\x48\x86\xf7\x14\x01\x05\x49",
84
+ 18: b"\x2a\x86\x48\x86\xf7\x14\x01\x04\x82\x31",
85
+ 19: b"\x09\x92\x26\x89\x93\xf2\x2c\x64",
86
+ 20: b"\x60\x86\x48\x01\x86\xf8\x42\x03",
87
+ 21: b"\x09\x92\x26\x89\x93\xf2\x2c\x64\x01",
88
+ 22: b"\x60\x86\x48\x01\x86\xf8\x42\x03\x01",
89
+ 23: b"\x2a\x86\x48\x86\xf7\x14\x01\x05\xb6\x58",
90
+ 24: b"\x55\x15",
91
+ 25: b"\x55\x12",
92
+ 26: b"\x55\x14",
93
+ 27: b"\x2b\x06\x01\x04\x01\x8b\x3a\x65\x77",
94
+ 28: b"\x60\x86\x48\x01\x86\xf8\x42\x03\x02",
95
+ 29: b"\x2b\x06\x01\x04\x01\x81\x7a\x01",
96
+ 30: b"\x2a\x86\x48\x86\xf7\x0d\x01\x09",
97
+ 31: b"\x09\x92\x26\x89\x93\xf2\x2c\x64\x04",
98
+ 32: b"\x2a\x86\x48\x86\xf7\x14\x01\x06\x17",
99
+ 33: b"\x2a\x86\x48\x86\xf7\x14\x01\x06\x12\x01",
100
+ 34: b"\x2a\x86\x48\x86\xf7\x14\x01\x06\x12\x02",
101
+ 35: b"\x2a\x86\x48\x86\xf7\x14\x01\x06\x0d\x03",
102
+ 36: b"\x2a\x86\x48\x86\xf7\x14\x01\x06\x0d\x04",
103
+ 37: b"\x2b\x06\x01\x01\x01\x01",
104
+ 38: b"\x2b\x06\x01\x01\x01\x02",
105
+ }
106
+
63
107
 
64
108
  class ClassEntry(NamedTuple):
65
109
  dnt: int
66
- oid: str
67
110
  id: int
68
111
  name: str
69
112
 
70
113
 
71
114
  class AttributeEntry(NamedTuple):
72
115
  dnt: int
73
- oid: str
74
116
  id: int
75
117
  name: str
76
118
  column: str
77
- type: str
119
+ syntax: int
78
120
  om_syntax: int | None
79
121
  om_object_class: bytes | None
80
122
  is_single_valued: bool
@@ -91,7 +133,6 @@ class Schema:
91
133
  def __init__(self):
92
134
  # Combined indices
93
135
  self._dnt_index: dict[int, ClassEntry | AttributeEntry] = {}
94
- self._oid_index: dict[str, ClassEntry | AttributeEntry] = {}
95
136
  self._attrtyp_index: dict[int, ClassEntry | AttributeEntry] = {}
96
137
 
97
138
  # Attribute specific indices
@@ -104,16 +145,23 @@ class Schema:
104
145
  self._class_id_index: dict[int, ClassEntry] = {}
105
146
  self._class_name_index: dict[str, ClassEntry] = {}
106
147
 
148
+ # OID prefixes
149
+ self._oid_idx_to_prefix_index: dict[int, str] = {
150
+ idx: decode_oid(prefix) for idx, prefix in BOOTSTRAP_OID_PREFIXES.items()
151
+ }
152
+ self._oid_prefix_to_idx_index: dict[str, int] = {
153
+ prefix: idx for idx, prefix in self._oid_idx_to_prefix_index.items()
154
+ }
155
+
107
156
  # Bootstrap fixed database columns (these do not exist in the schema)
108
- for ldap_name, column_name, syntax in BOOTSTRAP_COLUMNS:
157
+ for ldap_name, column_name, attrtyp in BOOTSTRAP_COLUMNS:
109
158
  self._add(
110
159
  AttributeEntry(
111
160
  dnt=-1,
112
- oid="",
113
161
  id=-1,
114
162
  name=ldap_name,
115
163
  column=column_name,
116
- type=attrtyp_to_oid(syntax),
164
+ syntax=attrtyp & 0xFF,
117
165
  om_syntax=None,
118
166
  om_object_class=None,
119
167
  is_single_valued=True,
@@ -123,12 +171,12 @@ class Schema:
123
171
  )
124
172
 
125
173
  # Bootstrap initial attributes
126
- for name, id, attribute_syntax, is_single_valued in BOOTSTRAP_ATTRIBUTES:
174
+ for name, id, attrtyp, is_single_valued in BOOTSTRAP_ATTRIBUTES:
127
175
  self._add_attribute(
128
176
  dnt=-1,
129
177
  id=id,
130
178
  name=name,
131
- syntax=attribute_syntax,
179
+ syntax=attrtyp,
132
180
  om_syntax=None,
133
181
  om_object_class=None,
134
182
  is_single_valued=is_single_valued,
@@ -151,48 +199,48 @@ class Schema:
151
199
  db: The database instance to load the schema from.
152
200
  """
153
201
 
154
- def _iter(id: int) -> Iterator[Object]:
155
- # Use the ATTc0 (objectClass) index to iterate over all objects of the given objectClass
156
- # TODO: In the future, maybe use `DataTable._get_index`, but that's not fully implemented yet
157
- cursor = db.data.table.index("INDEX_00000000").cursor()
158
- end = cursor.seek([id + 1]).record()
159
-
160
- cursor.reset()
161
- cursor.seek([id])
162
-
163
- record = cursor.record()
164
- while record is not None and record != end:
165
- yield Object.from_record(db, record)
166
- record = cursor.next()
202
+ # Load the schema entries from the DMD object
203
+ # This _should_ have all the attribute and class schema entries
204
+ # We used to perform an index search on objectClass (ATTc0, INDEX_00000000), but apparently
205
+ # not all databases have this index
206
+ dmd = db.data.dmd()
167
207
 
168
208
  # We bootstrapped these earlier
169
209
  attribute_schema = self.lookup_class(name="attributeSchema")
170
210
  class_schema = self.lookup_class(name="classSchema")
171
211
 
172
- for obj in _iter(attribute_schema.id):
173
- self._add_attribute(
174
- dnt=obj.dnt,
175
- id=obj.get("attributeID", raw=True),
176
- name=obj.get("lDAPDisplayName"),
177
- syntax=obj.get("attributeSyntax", raw=True),
178
- om_syntax=obj.get("oMSyntax"),
179
- om_object_class=obj.get("oMObjectClass"),
180
- is_single_valued=obj.get("isSingleValued"),
181
- link_id=obj.get("linkId"),
182
- search_flags=obj.get("searchFlags"),
183
- )
212
+ for obj in dmd.children():
213
+ # Get as raw to avoid decoding the attribute and class schema entries before we know which is which
214
+ classes = obj.get("objectClass", raw=True)
215
+ if attribute_schema.id in classes:
216
+ self._add_attribute(
217
+ dnt=obj.dnt,
218
+ id=obj.get("attributeID", raw=True),
219
+ name=obj.get("lDAPDisplayName"),
220
+ syntax=obj.get("attributeSyntax", raw=True),
221
+ om_syntax=obj.get("oMSyntax"),
222
+ om_object_class=obj.get("oMObjectClass"),
223
+ is_single_valued=obj.get("isSingleValued"),
224
+ link_id=obj.get("linkId"),
225
+ search_flags=obj.get("searchFlags"),
226
+ )
184
227
 
185
- for obj in _iter(class_schema.id):
186
- self._add_class(
187
- dnt=obj.dnt,
188
- id=obj.get("governsID", raw=True),
189
- name=obj.get("lDAPDisplayName"),
190
- )
228
+ elif class_schema.id in classes:
229
+ self._add_class(
230
+ dnt=obj.dnt,
231
+ id=obj.get("governsID", raw=True),
232
+ name=obj.get("lDAPDisplayName"),
233
+ )
234
+
235
+ # Load user-defined OID prefixes
236
+ if (prefix_map := db.data.dmd().get("prefixMap")) is not None:
237
+ self._oid_idx_to_prefix_index.update(parse_prefix_map(prefix_map))
238
+ # Rebuild the reverse index
239
+ self._oid_prefix_to_idx_index = {prefix: idx for idx, prefix in self._oid_idx_to_prefix_index.items()}
191
240
 
192
241
  def _add_class(self, dnt: int, id: int, name: str) -> None:
193
242
  entry = ClassEntry(
194
243
  dnt=dnt,
195
- oid=attrtyp_to_oid(id),
196
244
  id=id,
197
245
  name=name,
198
246
  )
@@ -210,14 +258,12 @@ class Schema:
210
258
  link_id: int | None,
211
259
  search_flags: SearchFlags | None,
212
260
  ) -> None:
213
- type_oid = attrtyp_to_oid(syntax)
214
261
  entry = AttributeEntry(
215
262
  dnt=dnt,
216
- oid=attrtyp_to_oid(id),
217
263
  id=id,
218
264
  name=name,
219
- column=f"ATT{OID_TO_TYPE[type_oid]}{id}",
220
- type=type_oid,
265
+ column=f"ATT{chr(ord('a') + (syntax & 0xFFFF))}{id}",
266
+ syntax=syntax & 0xFF,
221
267
  om_syntax=om_syntax,
222
268
  om_object_class=om_object_class,
223
269
  is_single_valued=is_single_valued,
@@ -229,8 +275,6 @@ class Schema:
229
275
  def _add(self, entry: ClassEntry | AttributeEntry) -> None:
230
276
  if entry.dnt != -1:
231
277
  self._dnt_index[entry.dnt] = entry
232
- if entry.oid != "":
233
- self._oid_index[entry.oid] = entry
234
278
  if entry.id != -1:
235
279
  self._attrtyp_index[entry.id] = entry
236
280
 
@@ -311,6 +355,62 @@ class Schema:
311
355
 
312
356
  return None
313
357
 
358
+ def lookup_oid(self, oid: str) -> ClassEntry | AttributeEntry | None:
359
+ """Lookup a schema entry by OID.
360
+
361
+ Args:
362
+ oid: The OID to look up.
363
+
364
+ Returns:
365
+ The matching schema entry or ``None`` if not found.
366
+ """
367
+ parts = oid.split(".")
368
+ if len(parts) < 2:
369
+ return None
370
+
371
+ long_id = 0
372
+ prefix_length = 0
373
+ if len(parts) > 2 and int(parts[-2]) & 0x80:
374
+ prefix_length = len(parts) - 2
375
+ long_id = int(parts[-3]) >> 7
376
+ else:
377
+ prefix_length = len(parts) - 1
378
+
379
+ prefix = ".".join(parts[:prefix_length])
380
+ if (idx := self._oid_prefix_to_idx_index.get(prefix)) is None:
381
+ return None
382
+
383
+ attrtyp = idx << 16
384
+ if len(parts) == prefix_length + 2:
385
+ attrtyp += (int(parts[-2]) & 0x7F) << 7
386
+ if long_id:
387
+ attrtyp |= 0x8000
388
+
389
+ attrtyp += int(parts[-1])
390
+ return self._attrtyp_index.get(attrtyp)
391
+
392
+ def attrtyp_to_oid(self, attrtyp: int) -> str | None:
393
+ """Convert an ATTRTYP integer value to an OID string.
394
+
395
+ Args:
396
+ attrtyp: The ATTRTYP integer value to convert.
397
+
398
+ Returns:
399
+ The corresponding OID string or ``None`` if the ATTRTYP does not correspond to a valid OID.
400
+ """
401
+ if (prefix := self._oid_idx_to_prefix_index.get(attrtyp >> 16)) is None:
402
+ return None
403
+
404
+ parts = [prefix]
405
+
406
+ if attrtyp & 0xFFFF < 0x80:
407
+ parts.append(str(attrtyp & 0xFF))
408
+ else:
409
+ parts.append(str(((attrtyp & 0xFF80) >> 7) | 0x80))
410
+ parts.append(str(attrtyp & 0x7F))
411
+
412
+ return ".".join(parts)
413
+
314
414
  def lookup(
315
415
  self,
316
416
  *,
@@ -338,7 +438,7 @@ class Schema:
338
438
  return self._dnt_index.get(dnt)
339
439
 
340
440
  if oid is not None:
341
- return self._oid_index.get(oid)
441
+ return self.lookup_oid(oid)
342
442
 
343
443
  if attrtyp is not None:
344
444
  return self._attrtyp_index.get(attrtyp)
@@ -348,3 +448,51 @@ class Schema:
348
448
  return self._class_name_index.get(name) or self._attribute_name_index.get(name)
349
449
 
350
450
  return None
451
+
452
+
453
+ def parse_prefix_map(buf: bytes) -> dict[int, str]:
454
+ """Parse a prefix map.
455
+
456
+ Args:
457
+ buf: The buffer containing the prefix map data.
458
+ """
459
+ result = {}
460
+
461
+ fh = BytesIO(buf)
462
+ c_ds.uint32(fh) # Number of prefixes
463
+ total_size = c_ds.uint32(fh) # Total size
464
+
465
+ while fh.tell() < total_size:
466
+ index = c_ds.uint16(fh)
467
+ prefix_length = c_ds.uint16(fh)
468
+ prefix = fh.read(prefix_length)
469
+
470
+ result[index] = decode_oid(prefix)
471
+
472
+ return result
473
+
474
+
475
+ def decode_oid(buf: bytes) -> str:
476
+ """Decode a BER encoded OID.
477
+
478
+ Args:
479
+ buf: The buffer containing the BER encoded OID.
480
+ """
481
+ values = [*divmod(buf[0], 40)]
482
+
483
+ idx = 1
484
+ while idx < len(buf):
485
+ value = buf[idx] & 0x7F
486
+ while buf[idx] & 0x80:
487
+ value <<= 7
488
+ idx += 1
489
+
490
+ if idx >= len(buf):
491
+ break
492
+
493
+ value |= buf[idx] & 0x7F
494
+
495
+ values.append(value)
496
+ idx += 1
497
+
498
+ return ".".join(map(str, values))