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  }