gitlab.com/jokerrs1/Sia@v1.3.2/modules/host/network.go (about) 1 package host 2 3 // TODO: seems like there would be problems with the negotiation protocols if 4 // the renter tried something like 'form' or 'renew' but then the connections 5 // dropped after the host completed the transaction but before the host was 6 // able to send the host signatures for the transaction. 7 // 8 // Especially on a renew, the host choosing to hold the renter signatures 9 // hostage could be a pretty significant problem, and would require the renter 10 // to attempt a double-spend to either force the transaction onto the 11 // blockchain or to make sure that the host cannot abscond with the funds 12 // without commitment. 13 // 14 // Incentive for the host to do such a thing is pretty low - they will still 15 // have to keep all the files following a renew in order to get the money. 16 17 import ( 18 "net" 19 "sync/atomic" 20 "time" 21 22 "github.com/NebulousLabs/Sia/build" 23 "github.com/NebulousLabs/Sia/encoding" 24 "github.com/NebulousLabs/Sia/modules" 25 "github.com/NebulousLabs/Sia/types" 26 ) 27 28 // rpcSettingsDeprecated is a specifier for a deprecated settings request. 29 var rpcSettingsDeprecated = types.Specifier{'S', 'e', 't', 't', 'i', 'n', 'g', 's'} 30 31 // threadedUpdateHostname periodically runs 'managedLearnHostname', which 32 // checks if the host's hostname has changed, and makes an updated host 33 // announcement if so. 34 func (h *Host) threadedUpdateHostname(closeChan chan struct{}) { 35 defer close(closeChan) 36 for { 37 h.managedLearnHostname() 38 // Wait 30 minutes to check again. If the hostname is changing 39 // regularly (more than once a week), we want the host to be able to be 40 // seen as having 95% uptime. Every minute that the announcement is 41 // pointing to the wrong address is a minute of perceived downtime to 42 // the renters. 43 select { 44 case <-h.tg.StopChan(): 45 return 46 case <-time.After(time.Minute * 30): 47 continue 48 } 49 } 50 } 51 52 // threadedTrackWorkingStatus periodically checks if the host is working, 53 // where working is defined as having received 3 settings calls in the past 15 54 // minutes. 55 func (h *Host) threadedTrackWorkingStatus(closeChan chan struct{}) { 56 defer close(closeChan) 57 58 // Before entering the longer loop, try a greedy, faster attempt to verify 59 // that the host is working. 60 prevSettingsCalls := atomic.LoadUint64(&h.atomicSettingsCalls) 61 select { 62 case <-h.tg.StopChan(): 63 return 64 case <-time.After(workingStatusFirstCheck): 65 } 66 settingsCalls := atomic.LoadUint64(&h.atomicSettingsCalls) 67 68 // sanity check 69 if prevSettingsCalls > settingsCalls { 70 build.Severe("the host's settings calls decremented") 71 } 72 73 h.mu.Lock() 74 if settingsCalls-prevSettingsCalls >= workingStatusThreshold { 75 h.workingStatus = modules.HostWorkingStatusWorking 76 } 77 // First check is quick, don't set to 'not working' if host has not been 78 // contacted enough times. 79 h.mu.Unlock() 80 81 for { 82 prevSettingsCalls = atomic.LoadUint64(&h.atomicSettingsCalls) 83 select { 84 case <-h.tg.StopChan(): 85 return 86 case <-time.After(workingStatusFrequency): 87 } 88 settingsCalls = atomic.LoadUint64(&h.atomicSettingsCalls) 89 90 // sanity check 91 if prevSettingsCalls > settingsCalls { 92 build.Severe("the host's settings calls decremented") 93 continue 94 } 95 96 h.mu.Lock() 97 if settingsCalls-prevSettingsCalls >= workingStatusThreshold { 98 h.workingStatus = modules.HostWorkingStatusWorking 99 } else { 100 h.workingStatus = modules.HostWorkingStatusNotWorking 101 } 102 h.mu.Unlock() 103 } 104 } 105 106 // threadedTrackConnectabilityStatus periodically checks if the host is 107 // connectable at its netaddress. 108 func (h *Host) threadedTrackConnectabilityStatus(closeChan chan struct{}) { 109 defer close(closeChan) 110 111 // Wait briefly before checking the first time. This gives time for any port 112 // forwarding to complete. 113 select { 114 case <-h.tg.StopChan(): 115 return 116 case <-time.After(connectabilityCheckFirstWait): 117 } 118 119 for { 120 h.mu.RLock() 121 autoAddr := h.autoAddress 122 userAddr := h.settings.NetAddress 123 h.mu.RUnlock() 124 125 activeAddr := autoAddr 126 if userAddr != "" { 127 activeAddr = userAddr 128 } 129 130 dialer := &net.Dialer{ 131 Cancel: h.tg.StopChan(), 132 Timeout: connectabilityCheckTimeout, 133 } 134 conn, err := dialer.Dial("tcp", string(activeAddr)) 135 136 var status modules.HostConnectabilityStatus 137 if err != nil { 138 status = modules.HostConnectabilityStatusNotConnectable 139 } else { 140 conn.Close() 141 status = modules.HostConnectabilityStatusConnectable 142 } 143 h.mu.Lock() 144 h.connectabilityStatus = status 145 h.mu.Unlock() 146 147 select { 148 case <-h.tg.StopChan(): 149 return 150 case <-time.After(connectabilityCheckFrequency): 151 } 152 } 153 } 154 155 // initNetworking performs actions like port forwarding, and gets the 156 // host established on the network. 157 func (h *Host) initNetworking(address string) (err error) { 158 // Create the listener and setup the close procedures. 159 h.listener, err = h.dependencies.Listen("tcp", address) 160 if err != nil { 161 return err 162 } 163 // Automatically close the listener when h.tg.Stop() is called. 164 threadedListenerClosedChan := make(chan struct{}) 165 h.tg.OnStop(func() { 166 err := h.listener.Close() 167 if err != nil { 168 h.log.Println("WARN: closing the listener failed:", err) 169 } 170 171 // Wait until the threadedListener has returned to continue shutdown. 172 <-threadedListenerClosedChan 173 }) 174 175 // Set the initial working state of the host 176 h.workingStatus = modules.HostWorkingStatusChecking 177 178 // Set the initial connectability state of the host 179 h.connectabilityStatus = modules.HostConnectabilityStatusChecking 180 181 // Set the port. 182 _, port, err := net.SplitHostPort(h.listener.Addr().String()) 183 if err != nil { 184 return err 185 } 186 h.port = port 187 if build.Release == "testing" { 188 // Set the autoAddress to localhost for testing builds only. 189 h.autoAddress = modules.NetAddress(net.JoinHostPort("localhost", h.port)) 190 } 191 192 // Non-blocking, perform port forwarding and create the hostname discovery 193 // thread. 194 go func() { 195 // Add this function to the threadgroup, so that the logger will not 196 // disappear before port closing can be registered to the threadgrourp 197 // OnStop functions. 198 err := h.tg.Add() 199 if err != nil { 200 // If this goroutine is not run before shutdown starts, this 201 // codeblock is reachable. 202 return 203 } 204 defer h.tg.Done() 205 206 err = h.managedForwardPort(port) 207 if err != nil { 208 h.log.Println("ERROR: failed to forward port:", err) 209 } else { 210 // Clear the port that was forwarded at startup. 211 h.tg.OnStop(func() { 212 err := h.managedClearPort() 213 if err != nil { 214 h.log.Println("ERROR: failed to clear port:", err) 215 } 216 }) 217 } 218 219 threadedUpdateHostnameClosedChan := make(chan struct{}) 220 go h.threadedUpdateHostname(threadedUpdateHostnameClosedChan) 221 h.tg.OnStop(func() { 222 <-threadedUpdateHostnameClosedChan 223 }) 224 225 threadedTrackWorkingStatusClosedChan := make(chan struct{}) 226 go h.threadedTrackWorkingStatus(threadedTrackWorkingStatusClosedChan) 227 h.tg.OnStop(func() { 228 <-threadedTrackWorkingStatusClosedChan 229 }) 230 231 threadedTrackConnectabilityStatusClosedChan := make(chan struct{}) 232 go h.threadedTrackConnectabilityStatus(threadedTrackConnectabilityStatusClosedChan) 233 h.tg.OnStop(func() { 234 <-threadedTrackConnectabilityStatusClosedChan 235 }) 236 }() 237 238 // Launch the listener. 239 go h.threadedListen(threadedListenerClosedChan) 240 return nil 241 } 242 243 // threadedHandleConn handles an incoming connection to the host, typically an 244 // RPC. 245 func (h *Host) threadedHandleConn(conn net.Conn) { 246 err := h.tg.Add() 247 if err != nil { 248 return 249 } 250 defer h.tg.Done() 251 252 // Close the conn on host.Close or when the method terminates, whichever comes 253 // first. 254 connCloseChan := make(chan struct{}) 255 defer close(connCloseChan) 256 go func() { 257 select { 258 case <-h.tg.StopChan(): 259 case <-connCloseChan: 260 } 261 conn.Close() 262 }() 263 264 // Set an initial duration that is generous, but finite. RPCs can extend 265 // this if desired. 266 err = conn.SetDeadline(time.Now().Add(5 * time.Minute)) 267 if err != nil { 268 h.log.Println("WARN: could not set deadline on connection:", err) 269 return 270 } 271 272 // Read a specifier indicating which action is being called. 273 var id types.Specifier 274 if err := encoding.ReadObject(conn, &id, 16); err != nil { 275 atomic.AddUint64(&h.atomicUnrecognizedCalls, 1) 276 h.log.Debugf("WARN: incoming conn %v was malformed: %v", conn.RemoteAddr(), err) 277 return 278 } 279 280 switch id { 281 case modules.RPCDownload: 282 atomic.AddUint64(&h.atomicDownloadCalls, 1) 283 err = extendErr("incoming RPCDownload failed: ", h.managedRPCDownload(conn)) 284 case modules.RPCRenewContract: 285 atomic.AddUint64(&h.atomicRenewCalls, 1) 286 err = extendErr("incoming RPCRenewContract failed: ", h.managedRPCRenewContract(conn)) 287 case modules.RPCFormContract: 288 atomic.AddUint64(&h.atomicFormContractCalls, 1) 289 err = extendErr("incoming RPCFormContract failed: ", h.managedRPCFormContract(conn)) 290 case modules.RPCReviseContract: 291 atomic.AddUint64(&h.atomicReviseCalls, 1) 292 err = extendErr("incoming RPCReviseContract failed: ", h.managedRPCReviseContract(conn)) 293 case modules.RPCSettings: 294 atomic.AddUint64(&h.atomicSettingsCalls, 1) 295 err = extendErr("incoming RPCSettings failed: ", h.managedRPCSettings(conn)) 296 case rpcSettingsDeprecated: 297 h.log.Debugln("Received deprecated settings call") 298 default: 299 h.log.Debugf("WARN: incoming conn %v requested unknown RPC \"%v\"", conn.RemoteAddr(), id) 300 atomic.AddUint64(&h.atomicUnrecognizedCalls, 1) 301 } 302 if err != nil { 303 atomic.AddUint64(&h.atomicErroredCalls, 1) 304 err = extendErr("error with "+conn.RemoteAddr().String()+": ", err) 305 h.managedLogError(err) 306 } 307 } 308 309 // listen listens for incoming RPCs and spawns an appropriate handler for each. 310 func (h *Host) threadedListen(closeChan chan struct{}) { 311 defer close(closeChan) 312 313 // Receive connections until an error is returned by the listener. When an 314 // error is returned, there will be no more calls to receive. 315 for { 316 // Block until there is a connection to handle. 317 conn, err := h.listener.Accept() 318 if err != nil { 319 return 320 } 321 322 go h.threadedHandleConn(conn) 323 324 // Soft-sleep to ratelimit the number of incoming connections. 325 select { 326 case <-h.tg.StopChan(): 327 case <-time.After(rpcRatelimit): 328 } 329 } 330 } 331 332 // NetAddress returns the address at which the host can be reached. 333 func (h *Host) NetAddress() modules.NetAddress { 334 h.mu.RLock() 335 defer h.mu.RUnlock() 336 337 if h.settings.NetAddress != "" { 338 return h.settings.NetAddress 339 } 340 return h.autoAddress 341 } 342 343 // NetworkMetrics returns information about the types of rpc calls that have 344 // been made to the host. 345 func (h *Host) NetworkMetrics() modules.HostNetworkMetrics { 346 h.mu.RLock() 347 defer h.mu.RUnlock() 348 return modules.HostNetworkMetrics{ 349 DownloadCalls: atomic.LoadUint64(&h.atomicDownloadCalls), 350 ErrorCalls: atomic.LoadUint64(&h.atomicErroredCalls), 351 FormContractCalls: atomic.LoadUint64(&h.atomicFormContractCalls), 352 RenewCalls: atomic.LoadUint64(&h.atomicRenewCalls), 353 ReviseCalls: atomic.LoadUint64(&h.atomicReviseCalls), 354 SettingsCalls: atomic.LoadUint64(&h.atomicSettingsCalls), 355 UnrecognizedCalls: atomic.LoadUint64(&h.atomicUnrecognizedCalls), 356 } 357 }