seekia/internal/network/peerServer/peerServer.go

330 lines
9 KiB
Go

// 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()
}