github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/helper/raftutil/msgpack.go (about)

     1  package raftutil
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"time"
     7  	"unicode"
     8  
     9  	"github.com/hashicorp/go-msgpack/codec"
    10  	"github.com/hashicorp/nomad/nomad/structs"
    11  )
    12  
    13  // fixTime converts any suspected time.Time binary string representation to time.Time
    14  func fixTime(v interface{}) {
    15  	switch v2 := v.(type) {
    16  	case map[string]interface{}:
    17  		for ek, ev := range v2 {
    18  			if s, ok := ev.(string); ok {
    19  				t, err := maybeDecodeTime(s)
    20  				if err == nil && isReasonableTime(t) {
    21  					v2[ek] = *t
    22  				}
    23  			} else {
    24  				fixTime(ev)
    25  			}
    26  		}
    27  	case []interface{}:
    28  		for _, e := range v2 {
    29  			fixTime(e)
    30  		}
    31  	default:
    32  		return
    33  	}
    34  }
    35  
    36  // maybeDecodeTime returns a time.Time representation if the string represents a msgpack
    37  // representation of a date.
    38  func maybeDecodeTime(v string) (*time.Time, error) {
    39  	if isASCII(v) {
    40  		return nil, fmt.Errorf("simple ascii string")
    41  	}
    42  
    43  	tt := &time.Time{}
    44  	var err error
    45  
    46  	err = tt.UnmarshalBinary([]byte(v))
    47  	if err == nil {
    48  		return tt, nil
    49  	}
    50  
    51  	switch len(v) {
    52  	case 4, 8, 12:
    53  	default:
    54  		return nil, fmt.Errorf("bad length: %d", len(v))
    55  	}
    56  
    57  	var nb bytes.Buffer
    58  	err = codec.NewEncoder(&nb, structs.MsgpackHandle).Encode(v)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	err = codec.NewDecoder(&nb, structs.MsgpackHandle).Decode(tt)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	return tt, nil
    69  }
    70  
    71  // isASCII returns true if all string characters are ASCII characters
    72  func isASCII(s string) bool {
    73  	for i := 0; i < len(s); i++ {
    74  		if s[i] > unicode.MaxASCII {
    75  			return false
    76  		}
    77  	}
    78  
    79  	return true
    80  }
    81  
    82  // isReasonableTime returns true if the time is within some N years of current time
    83  //
    84  // It's can be used to rule out bad date interpretation (e.g. dates million years away).
    85  func isReasonableTime(t *time.Time) bool {
    86  	if t.IsZero() {
    87  		return true
    88  	}
    89  
    90  	now := time.Now()
    91  	return t.Before(now.AddDate(20, 0, 0)) && t.After(now.AddDate(-20, 0, 0))
    92  }