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  }