// peerServer provides functions to start and stop a host's server. package peerServer //TODO: Make sure this package works import "seekia/internal/logger" import "seekia/internal/helpers" import "seekia/internal/mySettings" import "seekia/internal/network/appNetworkType/getAppNetworkType" import "seekia/internal/network/peerConnection" import "seekia/internal/network/craftResponses" import "sync" import "net" import "time" import "errors" // We lock this mutex whenever we start or stop the server var startOrStopServerMutex sync.Mutex var torServerIsRunningBoolMutex sync.RWMutex var torServerIsRunningBool bool var clearnetServerIsRunningBoolMutex sync.RWMutex var clearnetServerIsRunningBool bool func getTorServerIsRunningBool()bool{ torServerIsRunningBoolMutex.RLock() currentStatus := torServerIsRunningBool torServerIsRunningBoolMutex.RUnlock() return currentStatus } func getClearnetServerIsRunningBool()bool{ clearnetServerIsRunningBoolMutex.RLock() currentStatus := clearnetServerIsRunningBool clearnetServerIsRunningBoolMutex.RUnlock() return currentStatus } func setClearnetServerIsRunningBool(newStatus bool){ clearnetServerIsRunningBoolMutex.Lock() clearnetServerIsRunningBool = newStatus clearnetServerIsRunningBoolMutex.Unlock() } func setTorServerIsRunningBool(newStatus bool){ torServerIsRunningBoolMutex.Lock() torServerIsRunningBool = newStatus torServerIsRunningBoolMutex.Unlock() } // If host mode is enabled, this will be called automatically every 3 seconds by backgroundJobs // If the server is already running, we return nil func StartPeerServer()error{ startOrStopServerMutex.Lock() defer startOrStopServerMutex.Unlock() exists, hostModeStatus, err := mySettings.GetSetting("HostModeOnOffStatus") if (err != nil) { return err } if (exists == false || hostModeStatus != "On"){ return nil } //TODO: Make sure Host identity exists, and connectivity checks (example: check.torproject.org) //TODO: Remove this return once this is ready return nil startServerFunction := func(torOrClearnet string)error{ if (torOrClearnet != "Tor" && torOrClearnet != "Clearnet"){ return errors.New("startServerFunction called with invalid torOrClearnet: " + torOrClearnet) } getCurrentServerIsRunningStatus := func()bool{ if (torOrClearnet == "Tor"){ currentStatus := getTorServerIsRunningBool() return currentStatus } currentStatus := getClearnetServerIsRunningBool() return currentStatus } currentRunningStatus := getCurrentServerIsRunningStatus() if (currentRunningStatus == true){ return nil } getServerListenerObject := func()(net.Listener, error){ //if (torOrClearnet == "Tor"){ // //TODO // return nil, nil //} getMyClearnetPort := func()(string, error){ exists, myPort, err := mySettings.GetSetting("HostOverClearnetPort") if (err != nil) { return "", err } if (exists == false){ return "10321", nil } myPortInt, err := helpers.ConvertStringToInt(myPort) if (err != nil) { return "", errors.New("MySettings corrupt: Contains invalid HostOverClearnetPort: " + myPort) } if (myPortInt < 1 || myPortInt > 49151){ return "", errors.New("MySettings corrupt: Contains invalid HostOverClearnetPort: " + myPort) } return myPort, nil } myClearnetPort, err := getMyClearnetPort() if (err != nil) { return nil, err } newListenerObject, err := net.Listen("tcp", "127.0.0.1:" + myClearnetPort) if (err != nil) { return nil, err } return newListenerObject, nil } serverListenerObject, err := getServerListenerObject() if (err != nil){ return errors.New("Server Error: " + err.Error()) } // This loop will wait for new connections, and handle them when they arrive runServerLoop := func(){ for { newConnectionObject, err := serverListenerObject.Accept() if (err != nil) { logger.AddLogEntry("Network", "Server Error:" + err.Error()) break } go handleServerConnection(newConnectionObject) } serverListenerObject.Close() if (torOrClearnet == "Tor"){ setTorServerIsRunningBool(false) } else { setClearnetServerIsRunningBool(false) } } // This loop will check if we have shutdown the server // If we have, it will shutdown the serverListenerObject runServerShutdownLoop := func(){ for { if (torOrClearnet == "Tor"){ torServerIsRunning := getTorServerIsRunningBool() if (torServerIsRunning == false){ break } } else { clearnetServerIsRunning := getClearnetServerIsRunningBool() if (clearnetServerIsRunning == false){ break } } time.Sleep(time.Millisecond * 500) } serverListenerObject.Close() } if (torOrClearnet == "Tor"){ setTorServerIsRunningBool(true) } else { setClearnetServerIsRunningBool(true) } go runServerLoop() go runServerShutdownLoop() return nil } settingExists, hostOverClearnetStatus, err := mySettings.GetSetting("HostOverClearnetOnOffStatus") if (err != nil) { return err } if (settingExists == true && hostOverClearnetStatus == "On"){ err := startServerFunction("Clearnet") if (err != nil){ return err } } settingExists, hostOverTorStatus, err := mySettings.GetSetting("HostOverTorOnOffStatus") if (err != nil) { return err } if (settingExists == false || hostOverTorStatus != "Off"){ err := startServerFunction("Tor") if (err != nil){ return err } } return nil } // If host mode is disabled, this will be called automatically every 3 seconds by backgroundJobs // If the server is not running, we return nil // We will also call this when the application is shutting down. Background jobs will stop calling it at that point. func StopPeerServer()error{ //TODO: Graceful shutdown of current connections // Finish sending data, and then close connection. // We just want to avoid the requestor from designating us as a malicious host startOrStopServerMutex.Lock() defer startOrStopServerMutex.Unlock() setClearnetServerIsRunningBool(false) setTorServerIsRunningBool(false) return nil } func handleServerConnection(inputConnection net.Conn){ handleConnection := func()error{ // First request should always be an EstablishConnectionKey request downloadSuccessful, requestBytes, err := peerConnection.GetRequestThroughConnection(inputConnection) if (err != nil){ return err } if (downloadSuccessful == false){ return nil } appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { return err } requestIsWellFormed, identityHashMatches, networkTypeMatches, establishKeyResponse, connectionKey, err := craftResponses.GetEstablishConnectionKeyResponse(requestBytes, appNetworkType) if (err != nil) { return err } if (requestIsWellFormed == false){ responseToSend := []byte("Invalid request.") _, err := peerConnection.SendResponseThroughConnection(inputConnection, responseToSend) if (err != nil){ return err } return nil } if (identityHashMatches == false){ // The requestor believes we have a different identity hash than we currently do. // This could be happening because we recently switched our identity // It could also be happening because someone else is sharing our IP address on their own host profile responseToSend := []byte("Contacted wrong host.") _, err := peerConnection.SendResponseThroughConnection(inputConnection, responseToSend) if (err != nil){ return err } return nil } if (networkTypeMatches == false){ // The requestor believes we are on a different network type than we are // This may be because we recently switched network types. responseToSend := []byte("Invalid network type.") _, err := peerConnection.SendResponseThroughConnection(inputConnection, responseToSend) if (err != nil){ return err } return nil } responseSentSuccessfully, err := peerConnection.SendResponseThroughConnection(inputConnection, establishKeyResponse) if (err != nil){ return err } if (responseSentSuccessfully == false){ return nil } // Connection is established // Now we wait for new data to be sent for { downloadSuccessful, requestBytes, err := peerConnection.GetRequestThroughConnection(inputConnection) if (err != nil){ return err } if (downloadSuccessful == false){ return nil } responseBytes, err := craftResponses.GetServerResponseForRequest(requestBytes, appNetworkType, connectionKey) if (err != nil) { return err } responseSuccessful, err := peerConnection.SendResponseThroughConnection(inputConnection, responseBytes) if (err != nil) { return err } if (responseSuccessful == false){ return nil } //TODO: Deal with pruning old connections // We should keep a count of all active connections // If it exceeds a certain limit, we should stop accepting new connections and reply with a Busy message // We should also start dropping connections, starting with the ones that have been idle for the longest } } err := handleConnection() if (err != nil){ logger.AddLogEntry("Network", "Server Error: " + err.Error()) } inputConnection.Close() }