331 lines
9 KiB
Go
331 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()
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|