git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/mmdb/verifier.go (about) 1 package mmdb 2 3 import ( 4 "reflect" 5 "runtime" 6 ) 7 8 type verifier struct { 9 reader *Reader 10 } 11 12 // Verify checks that the database is valid. It validates the search tree, 13 // the data section, and the metadata section. This verifier is stricter than 14 // the specification and may return errors on databases that are readable. 15 func (r *Reader) Verify() error { 16 v := verifier{r} 17 if err := v.verifyMetadata(); err != nil { 18 return err 19 } 20 21 err := v.verifyDatabase() 22 runtime.KeepAlive(v.reader) 23 return err 24 } 25 26 func (v *verifier) verifyMetadata() error { 27 metadata := v.reader.Metadata 28 29 if metadata.BinaryFormatMajorVersion != 2 { 30 return testError( 31 "binary_format_major_version", 32 2, 33 metadata.BinaryFormatMajorVersion, 34 ) 35 } 36 37 if metadata.BinaryFormatMinorVersion != 0 { 38 return testError( 39 "binary_format_minor_version", 40 0, 41 metadata.BinaryFormatMinorVersion, 42 ) 43 } 44 45 if metadata.DatabaseType == "" { 46 return testError( 47 "database_type", 48 "non-empty string", 49 metadata.DatabaseType, 50 ) 51 } 52 53 if len(metadata.Description) == 0 { 54 return testError( 55 "description", 56 "non-empty slice", 57 metadata.Description, 58 ) 59 } 60 61 if metadata.IPVersion != 4 && metadata.IPVersion != 6 { 62 return testError( 63 "ip_version", 64 "4 or 6", 65 metadata.IPVersion, 66 ) 67 } 68 69 if metadata.RecordSize != 24 && 70 metadata.RecordSize != 28 && 71 metadata.RecordSize != 32 { 72 return testError( 73 "record_size", 74 "24, 28, or 32", 75 metadata.RecordSize, 76 ) 77 } 78 79 if metadata.NodeCount == 0 { 80 return testError( 81 "node_count", 82 "positive integer", 83 metadata.NodeCount, 84 ) 85 } 86 return nil 87 } 88 89 func (v *verifier) verifyDatabase() error { 90 offsets, err := v.verifySearchTree() 91 if err != nil { 92 return err 93 } 94 95 if err := v.verifyDataSectionSeparator(); err != nil { 96 return err 97 } 98 99 return v.verifyDataSection(offsets) 100 } 101 102 func (v *verifier) verifySearchTree() (map[uint]bool, error) { 103 offsets := make(map[uint]bool) 104 105 it := v.reader.Networks() 106 for it.Next() { 107 offset, err := v.reader.resolveDataPointer(it.lastNode.pointer) 108 if err != nil { 109 return nil, err 110 } 111 offsets[uint(offset)] = true 112 } 113 if err := it.Err(); err != nil { 114 return nil, err 115 } 116 return offsets, nil 117 } 118 119 func (v *verifier) verifyDataSectionSeparator() error { 120 separatorStart := v.reader.Metadata.NodeCount * v.reader.Metadata.RecordSize / 4 121 122 separator := v.reader.buffer[separatorStart : separatorStart+dataSectionSeparatorSize] 123 124 for _, b := range separator { 125 if b != 0 { 126 return newInvalidDatabaseError("unexpected byte in data separator: %v", separator) 127 } 128 } 129 return nil 130 } 131 132 func (v *verifier) verifyDataSection(offsets map[uint]bool) error { 133 pointerCount := len(offsets) 134 135 decoder := v.reader.decoder 136 137 var offset uint 138 bufferLen := uint(len(decoder.buffer)) 139 for offset < bufferLen { 140 var data any 141 rv := reflect.ValueOf(&data) 142 newOffset, err := decoder.decode(offset, rv, 0) 143 if err != nil { 144 return newInvalidDatabaseError( 145 "received decoding error (%v) at offset of %v", 146 err, 147 offset, 148 ) 149 } 150 if newOffset <= offset { 151 return newInvalidDatabaseError( 152 "data section offset unexpectedly went from %v to %v", 153 offset, 154 newOffset, 155 ) 156 } 157 158 pointer := offset 159 160 if _, ok := offsets[pointer]; !ok { 161 return newInvalidDatabaseError( 162 "found data (%v) at %v that the search tree does not point to", 163 data, 164 pointer, 165 ) 166 } 167 delete(offsets, pointer) 168 169 offset = newOffset 170 } 171 172 if offset != bufferLen { 173 return newInvalidDatabaseError( 174 "unexpected data at the end of the data section (last offset: %v, end: %v)", 175 offset, 176 bufferLen, 177 ) 178 } 179 180 if len(offsets) != 0 { 181 return newInvalidDatabaseError( 182 "found %v pointers (of %v) in the search tree that we did not see in the data section", 183 len(offsets), 184 pointerCount, 185 ) 186 } 187 return nil 188 } 189 190 func testError( 191 field string, 192 expected any, 193 actual any, 194 ) error { 195 return newInvalidDatabaseError( 196 "%v - Expected: %v Actual: %v", 197 field, 198 expected, 199 actual, 200 ) 201 }