github.com/iDigitalFlame/xmt@v0.5.4/device/id.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 package device 18 19 import ( 20 "io" 21 "os" 22 23 "github.com/iDigitalFlame/xmt/data" 24 "github.com/iDigitalFlame/xmt/util" 25 ) 26 27 const ( 28 // IDSize is the amount of bytes used to store the Host ID and 29 // SessionID values. The ID is the (HostID + SessionID). 30 IDSize = 32 31 // MachineIDSize is the amount of bytes that is used as the Host 32 // specific ID value that does not change when on the same host. 33 MachineIDSize = 28 34 ) 35 36 // ID is an alias for a byte array that represents a 32 byte client 37 // identification number. This is used for tracking and detection purposes. 38 // 39 // The first byte and the machine ID byte should NEVER be zero, otherwise it 40 // signals an invalid ID value or missing a random identifier. 41 type ID [IDSize]byte 42 43 // Empty returns true if this ID is considered empty. 44 func (i ID) Empty() bool { 45 return i[0] == 0 46 } 47 48 // Hash returns the 32bit hash sum of this ID value. The hash mechanism used is 49 // similar to the hash/fnv mechanism. 50 func (i ID) Hash() uint32 { 51 h := uint32(2166136261) 52 for x := range i { 53 h *= 16777619 54 h ^= uint32(i[x]) 55 } 56 return h 57 } 58 59 // Full returns the full string representation of this ID instance. 60 func (i ID) Full() string { 61 return i.string(0, IDSize) 62 } 63 64 // String returns a representation of this ID instance. 65 func (i ID) String() string { 66 if i[MachineIDSize] == 0 { 67 return i.string(0, MachineIDSize) 68 } 69 return i.string(MachineIDSize, IDSize) 70 } 71 72 // Seed will set the random portion of the ID value to the specified byte array 73 // value. 74 func (i *ID) Seed(b []byte) { 75 if len(b) == 0 { 76 return 77 } 78 copy(i[MachineIDSize:], b) 79 } 80 81 // Signature returns the signature portion of the ID value. This value is 82 // constant and unique for each device. 83 func (i ID) Signature() string { 84 return i.string(0, MachineIDSize) 85 } 86 87 // Load will attempt to load the Session UUID from the specified file. This 88 // function will return an error if the file cannot be read or not found. 89 func (i *ID) Load(s string) error { 90 // 0 - READONLY 91 r, err := os.OpenFile(s, 0, 0) 92 if err != nil { 93 return err 94 } 95 err = i.Read(r) 96 if r.Close(); err != nil { 97 return err 98 } 99 return nil 100 } 101 102 // Save will attempt to save the Session UUID to the specified file. This 103 // function will return an error if the file cannot be written to or created. 104 func (i ID) Save(s string) error { 105 // 0x242 - CREATE | TRUNCATE | RDWR 106 w, err := os.OpenFile(s, 0x242, 0644) 107 if err != nil { 108 return err 109 } 110 _, err = w.Write(i[:]) 111 if w.Close(); err != nil { 112 return err 113 } 114 return nil 115 } 116 117 // Read will attempt to read up to 'IDSize' bytes from the reader into this ID. 118 func (i *ID) Read(r io.Reader) error { 119 if n, err := io.ReadFull(r, i[:]); n != IDSize || i[0] == 0 { 120 // NOTE(dij): This line causes empty ID values to be considered read 121 // errors. Technically this is incorrect, but we should never 122 // be using empty ID values to communicate so it's sorta ok. 123 if err != nil { 124 return err 125 } 126 return io.ErrUnexpectedEOF 127 } 128 return nil 129 } 130 131 // Write will attempt to write the ID bytes into the supplied writer. 132 func (i ID) Write(w io.Writer) error { 133 n, err := w.Write(i[:]) 134 if err == nil && n != IDSize { 135 return io.ErrShortWrite 136 } 137 return err 138 } 139 func (i ID) string(start, end int) string { 140 var ( 141 b [64]byte 142 n int 143 ) 144 for x := start; x < end; x++ { 145 b[n] = util.HexTable[i[x]>>4] 146 b[n+1] = util.HexTable[i[x]&0x0F] 147 n += 2 148 } 149 return string(b[:n]) 150 } 151 152 // MarshalStream transform this struct into a binary format and writes to the 153 // supplied data.Writer. 154 func (i ID) MarshalStream(w data.Writer) error { 155 _, err := w.Write(i[:]) 156 return err 157 } 158 159 // UnmarshalStream transforms this struct from a binary format that is read from 160 // the supplied data.Reader. 161 func (i *ID) UnmarshalStream(r data.Reader) error { 162 return i.Read(r) 163 }