github.com/yggdrasil-network/yggdrasil-go@v0.5.6/src/core/nodeinfo.go (about) 1 package core 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "runtime" 9 "time" 10 11 iwt "github.com/Arceliar/ironwood/types" 12 "github.com/Arceliar/phony" 13 "github.com/yggdrasil-network/yggdrasil-go/src/version" 14 ) 15 16 type nodeinfo struct { 17 phony.Inbox 18 proto *protoHandler 19 myNodeInfo json.RawMessage 20 callbacks map[keyArray]nodeinfoCallback 21 } 22 23 type nodeinfoCallback struct { 24 call func(nodeinfo json.RawMessage) 25 created time.Time 26 } 27 28 // Initialises the nodeinfo cache/callback maps, and starts a goroutine to keep 29 // the cache/callback maps clean of stale entries 30 func (m *nodeinfo) init(proto *protoHandler) { 31 m.Act(nil, func() { 32 m._init(proto) 33 }) 34 } 35 36 func (m *nodeinfo) _init(proto *protoHandler) { 37 m.proto = proto 38 m.callbacks = make(map[keyArray]nodeinfoCallback) 39 m._cleanup() 40 } 41 42 func (m *nodeinfo) _cleanup() { 43 for boxPubKey, callback := range m.callbacks { 44 if time.Since(callback.created) > time.Minute { 45 delete(m.callbacks, boxPubKey) 46 } 47 } 48 time.AfterFunc(time.Second*30, func() { 49 m.Act(nil, m._cleanup) 50 }) 51 } 52 53 func (m *nodeinfo) _addCallback(sender keyArray, call func(nodeinfo json.RawMessage)) { 54 m.callbacks[sender] = nodeinfoCallback{ 55 created: time.Now(), 56 call: call, 57 } 58 } 59 60 // Handles the callback, if there is one 61 func (m *nodeinfo) _callback(sender keyArray, nodeinfo json.RawMessage) { 62 if callback, ok := m.callbacks[sender]; ok { 63 callback.call(nodeinfo) 64 delete(m.callbacks, sender) 65 } 66 } 67 68 func (m *nodeinfo) _getNodeInfo() json.RawMessage { 69 return m.myNodeInfo 70 } 71 72 // Set the current node's nodeinfo 73 func (m *nodeinfo) setNodeInfo(given map[string]interface{}, privacy bool) (err error) { 74 phony.Block(m, func() { 75 err = m._setNodeInfo(given, privacy) 76 }) 77 return 78 } 79 80 func (m *nodeinfo) _setNodeInfo(given map[string]interface{}, privacy bool) error { 81 newnodeinfo := make(map[string]interface{}, len(given)) 82 for k, v := range given { 83 newnodeinfo[k] = v 84 } 85 if !privacy { 86 newnodeinfo["buildname"] = version.BuildName() 87 newnodeinfo["buildversion"] = version.BuildVersion() 88 newnodeinfo["buildplatform"] = runtime.GOOS 89 newnodeinfo["buildarch"] = runtime.GOARCH 90 } 91 newjson, err := json.Marshal(newnodeinfo) 92 switch { 93 case err != nil: 94 return fmt.Errorf("NodeInfo marshalling failed: %w", err) 95 case len(newjson) > 16384: 96 return fmt.Errorf("NodeInfo exceeds max length of 16384 bytes") 97 default: 98 m.myNodeInfo = newjson 99 return nil 100 } 101 } 102 103 func (m *nodeinfo) sendReq(from phony.Actor, key keyArray, callback func(nodeinfo json.RawMessage)) { 104 m.Act(from, func() { 105 m._sendReq(key, callback) 106 }) 107 } 108 109 func (m *nodeinfo) _sendReq(key keyArray, callback func(nodeinfo json.RawMessage)) { 110 if callback != nil { 111 m._addCallback(key, callback) 112 } 113 _, _ = m.proto.core.PacketConn.WriteTo([]byte{typeSessionProto, typeProtoNodeInfoRequest}, iwt.Addr(key[:])) 114 } 115 116 func (m *nodeinfo) handleReq(from phony.Actor, key keyArray) { 117 m.Act(from, func() { 118 m._sendRes(key) 119 }) 120 } 121 122 func (m *nodeinfo) handleRes(from phony.Actor, key keyArray, info json.RawMessage) { 123 m.Act(from, func() { 124 m._callback(key, info) 125 }) 126 } 127 128 func (m *nodeinfo) _sendRes(key keyArray) { 129 bs := append([]byte{typeSessionProto, typeProtoNodeInfoResponse}, m._getNodeInfo()...) 130 _, _ = m.proto.core.PacketConn.WriteTo(bs, iwt.Addr(key[:])) 131 } 132 133 // Admin socket stuff 134 135 type GetNodeInfoRequest struct { 136 Key string `json:"key"` 137 } 138 type GetNodeInfoResponse map[string]json.RawMessage 139 140 func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) { 141 var req GetNodeInfoRequest 142 if err := json.Unmarshal(in, &req); err != nil { 143 return nil, err 144 } 145 if req.Key == "" { 146 return nil, fmt.Errorf("No remote public key supplied") 147 } 148 var key keyArray 149 var kbs []byte 150 var err error 151 if kbs, err = hex.DecodeString(req.Key); err != nil { 152 return nil, fmt.Errorf("Failed to decode public key: %w", err) 153 } 154 copy(key[:], kbs) 155 ch := make(chan []byte, 1) 156 m.sendReq(nil, key, func(info json.RawMessage) { 157 ch <- info 158 }) 159 timer := time.NewTimer(6 * time.Second) 160 defer timer.Stop() 161 select { 162 case <-timer.C: 163 return nil, errors.New("Timed out waiting for response") 164 case info := <-ch: 165 var msg json.RawMessage 166 if err := msg.UnmarshalJSON(info); err != nil { 167 return nil, err 168 } 169 key := hex.EncodeToString(kbs[:]) 170 res := GetNodeInfoResponse{key: msg} 171 return res, nil 172 } 173 }