From 4921a6345dd1f52949ccf62dce554f07a579c62c Mon Sep 17 00:00:00 2001 From: Akilan Date: Thu, 1 Dec 2022 05:37:15 +0000 Subject: [PATCH] added auto reverse proxy detection and applicable for docker container --- cmd/action.go | 520 +++++++++++++++++++++-------------------- cmd/flags.go | 444 ++++++++++++++++++----------------- config/config.go | 262 ++++++++++----------- p2p/frp/client.go | 128 ++++++++-- p2p/frp/server.go | 45 +++- p2p/frp/server_test.go | 29 ++- p2p/iptable.go | 329 +++++++++++++------------- server/server.go | 287 ++++++++++++++--------- 8 files changed, 1136 insertions(+), 908 deletions(-) diff --git a/cmd/action.go b/cmd/action.go index 21eec91..e6fa73a 100644 --- a/cmd/action.go +++ b/cmd/action.go @@ -1,294 +1,304 @@ package cmd import ( - "fmt" - "git.sr.ht/~akilan1999/p2p-rendering-computation/client" - "git.sr.ht/~akilan1999/p2p-rendering-computation/config" - "git.sr.ht/~akilan1999/p2p-rendering-computation/generate" - "git.sr.ht/~akilan1999/p2p-rendering-computation/p2p" - "git.sr.ht/~akilan1999/p2p-rendering-computation/plugin" - "git.sr.ht/~akilan1999/p2p-rendering-computation/server" - "github.com/urfave/cli/v2" + "fmt" + "git.sr.ht/~akilan1999/p2p-rendering-computation/client" + "git.sr.ht/~akilan1999/p2p-rendering-computation/config" + "git.sr.ht/~akilan1999/p2p-rendering-computation/generate" + "git.sr.ht/~akilan1999/p2p-rendering-computation/p2p" + "git.sr.ht/~akilan1999/p2p-rendering-computation/p2p/frp" + "git.sr.ht/~akilan1999/p2p-rendering-computation/plugin" + "git.sr.ht/~akilan1999/p2p-rendering-computation/server" + "github.com/urfave/cli/v2" ) var CliAction = func(ctx *cli.Context) error { - if Server { - // TODO: with RPC calls - server.Server() - //server.Rpc() - } + if Server { + err := server.Server() + if err != nil { + fmt.Print(err) + } + //server.Rpc() + } - //Listing servers and also updates IP tables (Default 3 hops) - if UpdateServerList { - err := client.UpdateIpTableListClient() - if err != nil { - fmt.Print(err) - } - // Reads from ip table and passes it - // on to struct print function - //Servers, err := p2p.ReadIpTable() - //if err != nil { - // return err - //} - //client.PrettyPrint(Servers) - p2p.PrintIpTable() - } + //Listing servers and also updates IP tables (Default 3 hops) + if UpdateServerList { + err := client.UpdateIpTableListClient() + if err != nil { + fmt.Print(err) + } + // Reads from ip table and passes it + // on to struct print function + //Servers, err := p2p.ReadIpTable() + //if err != nil { + // return err + //} + //client.PrettyPrint(Servers) + p2p.PrintIpTable() + } - // Displays the IP table - if ServerList { - // Reads from ip table and passes it - // on to struct print function - //Servers, err := p2p.ReadIpTable() - //if err != nil { - // return err - //} - p2p.PrintIpTable() - } + // Displays the IP table + if ServerList { + // Reads from ip table and passes it + // on to struct print function + //Servers, err := p2p.ReadIpTable() + //if err != nil { + // return err + //} + p2p.PrintIpTable() + } - // Add provided IP to the IP table - if AddServer != "" { - res, err := p2p.ReadIpTable() - if err != nil { - fmt.Println(err) - } + // Add provided IP to the IP table + if AddServer != "" { + res, err := p2p.ReadIpTable() + if err != nil { + fmt.Println(err) + } - //Create variable of type IpAddress and set IP address - // to it - var IpAddr p2p.IpAddress + //Create variable of type IpAddress and set IP address + // to it + var IpAddr p2p.IpAddress - //Checking if the address is a ipv4 - // or ipv6 address - ip4Orip6 := p2p.Ip4or6(AddServer) - if ip4Orip6 == "version 6" { - IpAddr.Ipv6 = AddServer - } else { - IpAddr.Ipv4 = AddServer - } + //Checking if the address is a ipv4 + // or ipv6 address + ip4Orip6 := p2p.Ip4or6(AddServer) + if ip4Orip6 == "version 6" { + IpAddr.Ipv6 = AddServer + } else { + IpAddr.Ipv4 = AddServer + } - // If a server port is provided then set it - if Ports != "" { - IpAddr.ServerPort = Ports - } else { - IpAddr.ServerPort = "8088" - } - // Append IP address to variable result which - // is a list - res.IpAddress = append(res.IpAddress, IpAddr) - // Adds the new server IP to the iptable - res.WriteIpTable() + // If a server port is provided then set it + if Ports != "" { + IpAddr.ServerPort = Ports + } else { + IpAddr.ServerPort = "8088" + } + // Append IP address to variable result which + // is a list + res.IpAddress = append(res.IpAddress, IpAddr) + // Adds the new server IP to the iptable + res.WriteIpTable() + } - } + // Displays all images available on the server side + if ViewImages != "" { + imageRes, err := client.ViewContainers(ViewImages) - // Displays all images available on the server side - if ViewImages != "" { - imageRes, err := client.ViewContainers(ViewImages) + if err != nil { + fmt.Print(err) + } + client.PrettyPrint(imageRes) + } - if err != nil { - fmt.Print(err) - } - client.PrettyPrint(imageRes) - } + // Function called to stop and remove server from Docker + if RemoveVM != "" && ID != "" { + err := client.RemoveContianer(RemoveVM, ID) + if err != nil { + fmt.Print(err) + } + } - // Function called to stop and remove server from Docker - if RemoveVM != "" && ID != "" { - err := client.RemoveContianer(RemoveVM,ID) - if err != nil { - fmt.Print(err) - } - } + //Call function to create Docker container + if CreateVM != "" { - //Call function to create Docker container - if CreateVM != "" { + var PortsInt int - var PortsInt int + if Ports != "" { + // Convert Get Request value to int + fmt.Sscanf(Ports, "%d", &PortsInt) + } - if Ports != "" { - // Convert Get Request value to int - fmt.Sscanf(Ports, "%d", &PortsInt) - } + // Calls function to do Api call to start the container on the server side + imageRes, err := client.StartContainer(CreateVM, PortsInt, GPU, ContainerName) - // Calls function to do Api call to start the container on the server side - imageRes, err := client.StartContainer(CreateVM,PortsInt,GPU,ContainerName) + if err != nil { + fmt.Print(err) + } + client.PrettyPrint(imageRes) + } - if err != nil { - fmt.Print(err) - } - client.PrettyPrint(imageRes) - } + //Call if specs flag is called + if Specs != "" { + specs, err := client.GetSpecs(Specs) + if err != nil { + return err + } - //Call if specs flag is called - if Specs != "" { - specs, err := client.GetSpecs(Specs) - if err != nil { - return err - } + // Pretty print + client.PrettyPrint(specs) + } - // Pretty print - client.PrettyPrint(specs) - } + //Sets default paths to the config file + if SetDefaultConfig { + err := config.SetDefaults() + if err != nil { + fmt.Print(err) + } + } - //Sets default paths to the config file - if SetDefaultConfig { - err := config.SetDefaults() - if err != nil { - fmt.Print(err) - } - } + //If the network interface flag is called + // Then all the network interfaces are displayed + if NetworkInterface { + err := p2p.ViewNetworkInterface() + if err != nil { + fmt.Print(err) + } + } - //If the network interface flag is called - // Then all the network interfaces are displayed - if NetworkInterface { - err := p2p.ViewNetworkInterface() - if err != nil { - fmt.Print(err) - } - } + // If the view plugin flag is called then display all + // plugins available + if ViewPlugin { + plugins, err := plugin.DetectPlugins() + if err != nil { + fmt.Print(err) + } + client.PrettyPrint(plugins) + } - // If the view plugin flag is called then display all - // plugins available - if ViewPlugin { - plugins ,err := plugin.DetectPlugins() - if err != nil { - fmt.Print(err) - } - client.PrettyPrint(plugins) - } + // If the flag Tracked Container is called or the flag + // --tc + if TrackedContainers { + err, trackedContainers := client.ViewTrackedContainers() + if err != nil { + fmt.Print(err) + } + client.PrettyPrint(trackedContainers) + } - // If the flag Tracked Container is called or the flag - // --tc - if TrackedContainers { - err, trackedContainers := client.ViewTrackedContainers() - if err != nil { - fmt.Print(err) - } - client.PrettyPrint(trackedContainers) - } + //Executing plugin when the plugin flag is called + // --plugin + if ExecutePlugin != "" { + // To execute plugin requires the container ID or group ID provided when being executed + if ID != "" { + err := plugin.CheckRunPlugin(ExecutePlugin, ID) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Success") + } + } else { + fmt.Println("provide container ID") + } - //Executing plugin when the plugin flag is called - // --plugin - if ExecutePlugin != "" { - // To execute plugin requires the container ID or group ID provided when being executed - if ID != "" { - err := plugin.CheckRunPlugin(ExecutePlugin, ID) - if err != nil { - fmt.Println(err) - } else { - fmt.Println("Success") - } - } else { - fmt.Println("provide container ID") - } + } - } + // Executing function to create new group + // Creates new group and outputs JSON file + if CreateGroup { + group, err := client.CreateGroup() + if err != nil { + return err + } + client.PrettyPrint(group) + } - // Executing function to create new group - // Creates new group and outputs JSON file - if CreateGroup { - group, err := client.CreateGroup() - if err != nil { - return err - } - client.PrettyPrint(group) - } + // Actions to be performed when the + // group flag is called + // --group + if Group != "" { + // Remove container from group based on group ID provided + // --rmcgroup --id + if RemoveContainerGroup && ID != "" { + group, err := client.RemoveContainerGroup(ID, Group) + if err != nil { + fmt.Println(err) + } else { + client.PrettyPrint(group) + } + } else if ID != "" { // Add container to group based on group ID provided + // --id + group, err := client.AddContainerToGroup(ID, Group) + if err != nil { + fmt.Println(err) + } else { + client.PrettyPrint(group) + } + } else { // View all information about current group + group, err := client.GetGroup(Group) + if err != nil { + fmt.Println(err) + } else { + client.PrettyPrint(group) + } + } + } - // Actions to be performed when the - // group flag is called - // --group - if Group != "" { - // Remove container from group based on group ID provided - // --rmcgroup --id - if RemoveContainerGroup && ID != "" { - group, err := client.RemoveContainerGroup(ID, Group) - if err != nil { - fmt.Println(err) - } else { - client.PrettyPrint(group) - } - } else if ID != "" { // Add container to group based on group ID provided - // --id - group, err := client.AddContainerToGroup(ID,Group) - if err != nil { - fmt.Println(err) - } else { - client.PrettyPrint(group) - } - } else { // View all information about current group - group, err := client.GetGroup(Group) - if err != nil { - fmt.Println(err) - } else { - client.PrettyPrint(group) - } - } - } + // Execute function to remove entire group + // when remove group flag is called + // --rmgroup + if RemoveGroup != "" { + err := client.RemoveGroup(RemoveGroup) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Group Removed") + } + } - // Execute function to remove entire group - // when remove group flag is called - // --rmgroup - if RemoveGroup != "" { - err := client.RemoveGroup(RemoveGroup) - if err != nil { - fmt.Println(err) - } else { - fmt.Println("Group Removed") - } - } + // Execute Function to view all groups + if Groups { + groups, err := client.ReadGroup() + if err != nil { + fmt.Println(err) + } else { + client.PrettyPrint(groups) + } + } - // Execute Function to view all groups - if Groups { - groups, err := client.ReadGroup() - if err != nil { - fmt.Println(err) - } else { - client.PrettyPrint(groups) - } - } - // -- REMOVE ON REGULAR RELEASE -- - // when flag --gen is called an extension - // of the project is created to repurpose - // the project for custom purpose - if Generate != "" { - var err error - // If the module name is provided - if Modulename != "" { - err = generate.GenerateNewProject(Generate,Modulename) - } else { - err = generate.GenerateNewProject(Generate,Generate) - } - if err != nil { - fmt.Println(err) - } else { - fmt.Println("Created new folder: " + Generate) - fmt.Println("1. Enter inside " + Generate + " directory") - fmt.Println("2. git remote add " + Generate + " ") - fmt.Println("3. git push " + Generate + " ") - fmt.Println("4. go mod tidy") - fmt.Println("5. sh install.sh " + Generate) - fmt.Println("6. ./" + Generate + " -h (This is to test if the binary is working)") - } - } - //-------------------------------- + // Starts server as a reverse proxy so that + // nodes can connect to each other behind NAT + if FRPProxy { + err := frp.StartFRPProxyFromConfig() + if err != nil { + fmt.Println(err) + } + } - if PullPlugin != "" { - err := plugin.DownloadPlugin(PullPlugin) - if err != nil { - fmt.Println(err) - } else { - fmt.Println("Success") - } - } + // -- REMOVE ON REGULAR RELEASE -- + // when flag --gen is called an extension + // of the project is created to repurpose + // the project for custom purpose + if Generate != "" { + var err error + // If the module name is provided + if Modulename != "" { + err = generate.GenerateNewProject(Generate, Modulename) + } else { + err = generate.GenerateNewProject(Generate, Generate) + } + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Created new folder: " + Generate) + fmt.Println("1. Enter inside " + Generate + " directory") + fmt.Println("2. git remote add " + Generate + " ") + fmt.Println("3. git push " + Generate + " ") + fmt.Println("4. go mod tidy") + fmt.Println("5. sh install.sh " + Generate) + fmt.Println("6. ./" + Generate + " -h (This is to test if the binary is working)") + } + } + //-------------------------------- - if RemovePlugin != "" { - err := plugin.DeletePlugin(RemovePlugin) - if err != nil { - fmt.Println(err) - } else { - fmt.Println("Success") - } - } + if PullPlugin != "" { + err := plugin.DownloadPlugin(PullPlugin) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Success") + } + } + if RemovePlugin != "" { + err := plugin.DeletePlugin(RemovePlugin) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Success") + } + } - - return nil + return nil } diff --git a/cmd/flags.go b/cmd/flags.go index d9feac6..eeb3686 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -1,228 +1,236 @@ package cmd import ( - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) // Variables declared for CLI var ( - AddServer string - ViewImages string - CreateVM string - ContainerName string - Ports string - Server bool - RemoveVM string - ID string - Specs string - GPU bool - UpdateServerList bool - ServerList bool - SetDefaultConfig bool - NetworkInterface bool - ViewPlugin bool - TrackedContainers bool - ExecutePlugin string - CreateGroup bool - Group string - Groups bool - RemoveContainerGroup bool - RemoveGroup string - // Generate only allowed in dev release - // -- REMOVE ON REGULAR RELEASE -- - Generate string - Modulename string - //-------------------------------- - PullPlugin string - RemovePlugin string + AddServer string + ViewImages string + CreateVM string + ContainerName string + Ports string + Server bool + RemoveVM string + ID string + Specs string + GPU bool + UpdateServerList bool + ServerList bool + SetDefaultConfig bool + NetworkInterface bool + ViewPlugin bool + TrackedContainers bool + ExecutePlugin string + CreateGroup bool + Group string + Groups bool + RemoveContainerGroup bool + RemoveGroup string + FRPProxy bool + // Generate only allowed in dev release + // -- REMOVE ON REGULAR RELEASE -- + Generate string + Modulename string + //-------------------------------- + PullPlugin string + RemovePlugin string ) var AppConfigFlags = []cli.Flag{ - // Deprecated to be implemented using GRPC - &cli.BoolFlag{ - Name: "Server", - Aliases: []string{"s"}, - Usage: "Starts server", - EnvVars: []string{"SERVER"}, - Destination: &Server, - }, - &cli.BoolFlag{ - Name: "UpdateServerList", - Aliases: []string{"us"}, - Usage: "Update List of Server available based on servers iptables", - EnvVars: []string{"UPDATE_SERVER_LIST"}, - Destination: &UpdateServerList, - }, - &cli.BoolFlag{ - Name: "ListServers", - Aliases: []string{"ls"}, - Usage: "List servers which can render tasks", - EnvVars: []string{"LIST_SERVERS"}, - Destination: &ServerList, - }, - &cli.StringFlag{ - Name: "AddServer", - Aliases: []string{"as"}, - Usage: "Adds server IP address to iptables", - EnvVars: []string{"ADD_SERVER"}, - Destination: &AddServer, - }, - &cli.StringFlag{ - Name: "ViewImages", - Aliases: []string{"vi"}, - Usage: "View images available on the server IP address", - EnvVars: []string{"VIEW_IMAGES"}, - Destination: &ViewImages, - }, - &cli.StringFlag{ - Name: "CreateVM", - Aliases: []string{"touch"}, - Usage: "Creates Docker container on the selected server", - EnvVars: []string{"CREATE_VM"}, - Destination: &CreateVM, - }, - &cli.StringFlag{ - Name: "ContainerName", - Aliases: []string{"cn"}, - Usage: "Specifying the container run on the server side", - EnvVars: []string{"CONTAINER_NAME"}, - Destination: &ContainerName, - }, - &cli.StringFlag{ - Name: "RemoveVM", - Aliases: []string{"rm"}, - Usage: "Stop and Remove Docker container", - EnvVars: []string{"REMOVE_VM"}, - Destination: &RemoveVM, - }, - &cli.StringFlag{ - Name: "ID", - Aliases: []string{"id"}, - Usage: "Docker Container ID", - EnvVars: []string{"ID"}, - Destination: &ID, - }, - &cli.StringFlag{ - Name: "Ports", - Aliases: []string{"p"}, - Usage: "Number of ports to open for the Docker Container", - EnvVars: []string{"NUM_PORTS"}, - Destination: &Ports, - }, - &cli.BoolFlag{ - Name: "GPU", - Aliases: []string{"gpu"}, - Usage: "Create Docker Containers to access GPU", - EnvVars: []string{"USE_GPU"}, - Destination: &GPU, - }, - &cli.StringFlag{ - Name: "Specification", - Aliases: []string{"specs"}, - Usage: "Specs of the server node", - EnvVars: []string{"SPECS"}, - Destination: &Specs, - }, - &cli.BoolFlag{ - Name: "SetDefaultConfig", - Aliases: []string{"dc"}, - Usage: "Sets a default configuration file", - EnvVars: []string{"SET_DEFAULT_CONFIG"}, - Destination: &SetDefaultConfig, - }, - &cli.BoolFlag{ - Name: "NetworkInterfaces", - Aliases: []string{"ni"}, - Usage: "Shows the network interface in your computer", - EnvVars: []string{"NETWORK_INTERFACE"}, - Destination: &NetworkInterface, - }, - &cli.BoolFlag{ - Name: "ViewPlugins", - Aliases: []string{"vp"}, - Usage: "Shows plugins available to be executed", - EnvVars: []string{"VIEW_PLUGIN"}, - Destination: &ViewPlugin, - }, - &cli.BoolFlag{ - Name: "TrackedContainers", - Aliases: []string{"tc"}, - Usage: "View containers which have " + - "been created from the client side ", - EnvVars: []string{"TRACKED_CONTAINERS"}, - Destination: &TrackedContainers, - }, - &cli.StringFlag{ - Name: "ExecutePlugin", - Aliases: []string{"plugin"}, - Usage: "Plugin which needs to be executed", - EnvVars: []string{"EXECUTE_PLUGIN"}, - Destination: &ExecutePlugin, - }, - &cli.BoolFlag{ - Name: "CreateGroup", - Aliases: []string{"cgroup"}, - Usage: "Creates a new group", - EnvVars: []string{"CREATE_GROUP"}, - Destination: &CreateGroup, - }, - &cli.StringFlag{ - Name: "Group", - Aliases: []string{"group"}, - Usage: "group flag with argument group ID", - EnvVars: []string{"GROUP"}, - Destination: &Group, - }, - &cli.BoolFlag{ - Name: "Groups", - Aliases: []string{"groups"}, - Usage: "View all groups", - EnvVars: []string{"GROUPS"}, - Destination: &Groups, - }, - &cli.BoolFlag{ - Name: "RemoveContainerGroup", - Aliases: []string{"rmcgroup"}, - Usage: "Remove specific container in the group", - EnvVars: []string{"REMOVE_CONTAINER_GROUP"}, - Destination: &RemoveContainerGroup, - }, - &cli.StringFlag{ - Name: "RemoveGroup", - Aliases: []string{"rmgroup"}, - Usage: "Removes the entire group", - EnvVars: []string{"REMOVE_GROUP"}, - Destination: &RemoveGroup, - }, - // Generate only allowed in dev release - // -- REMOVE ON REGULAR RELEASE -- - &cli.StringFlag{ - Name: "Generate", - Aliases: []string{"gen"}, - Usage: "Generates a new copy of P2PRC which can be modified based on your needs", - EnvVars: []string{"GENERATE"}, - Destination: &Generate, - }, - &cli.StringFlag{ - Name: "ModuleName", - Aliases: []string{"mod"}, - Usage: "New go project module name", - EnvVars: []string{"MODULENAME"}, - Destination: &Modulename, - }, - //-------------------------------- - &cli.StringFlag{ - Name: "PullPlugin", - Aliases: []string{"pp"}, - Usage: "Pulls plugin from git repos", - EnvVars: []string{"PULLPLUGIN"}, - Destination: &PullPlugin, - }, - &cli.StringFlag{ - Name: "RemovePlugin", - Aliases: []string{"rp"}, - Usage: "Removes plugin", - EnvVars: []string{"REMOVEPLUGIN"}, - Destination: &RemovePlugin, - }, -} \ No newline at end of file + // Deprecated to be implemented using GRPC + &cli.BoolFlag{ + Name: "Server", + Aliases: []string{"s"}, + Usage: "Starts server", + EnvVars: []string{"SERVER"}, + Destination: &Server, + }, + &cli.BoolFlag{ + Name: "UpdateServerList", + Aliases: []string{"us"}, + Usage: "Update List of Server available based on servers iptables", + EnvVars: []string{"UPDATE_SERVER_LIST"}, + Destination: &UpdateServerList, + }, + &cli.BoolFlag{ + Name: "ListServers", + Aliases: []string{"ls"}, + Usage: "List servers which can render tasks", + EnvVars: []string{"LIST_SERVERS"}, + Destination: &ServerList, + }, + &cli.StringFlag{ + Name: "AddServer", + Aliases: []string{"as"}, + Usage: "Adds server IP address to iptables", + EnvVars: []string{"ADD_SERVER"}, + Destination: &AddServer, + }, + &cli.StringFlag{ + Name: "ViewImages", + Aliases: []string{"vi"}, + Usage: "View images available on the server IP address", + EnvVars: []string{"VIEW_IMAGES"}, + Destination: &ViewImages, + }, + &cli.StringFlag{ + Name: "CreateVM", + Aliases: []string{"touch"}, + Usage: "Creates Docker container on the selected server", + EnvVars: []string{"CREATE_VM"}, + Destination: &CreateVM, + }, + &cli.StringFlag{ + Name: "ContainerName", + Aliases: []string{"cn"}, + Usage: "Specifying the container run on the server side", + EnvVars: []string{"CONTAINER_NAME"}, + Destination: &ContainerName, + }, + &cli.StringFlag{ + Name: "RemoveVM", + Aliases: []string{"rm"}, + Usage: "Stop and Remove Docker container", + EnvVars: []string{"REMOVE_VM"}, + Destination: &RemoveVM, + }, + &cli.StringFlag{ + Name: "ID", + Aliases: []string{"id"}, + Usage: "Docker Container ID", + EnvVars: []string{"ID"}, + Destination: &ID, + }, + &cli.StringFlag{ + Name: "Ports", + Aliases: []string{"p"}, + Usage: "Number of ports to open for the Docker Container", + EnvVars: []string{"NUM_PORTS"}, + Destination: &Ports, + }, + &cli.BoolFlag{ + Name: "GPU", + Aliases: []string{"gpu"}, + Usage: "Create Docker Containers to access GPU", + EnvVars: []string{"USE_GPU"}, + Destination: &GPU, + }, + &cli.StringFlag{ + Name: "Specification", + Aliases: []string{"specs"}, + Usage: "Specs of the server node", + EnvVars: []string{"SPECS"}, + Destination: &Specs, + }, + &cli.BoolFlag{ + Name: "SetDefaultConfig", + Aliases: []string{"dc"}, + Usage: "Sets a default configuration file", + EnvVars: []string{"SET_DEFAULT_CONFIG"}, + Destination: &SetDefaultConfig, + }, + &cli.BoolFlag{ + Name: "NetworkInterfaces", + Aliases: []string{"ni"}, + Usage: "Shows the network interface in your computer", + EnvVars: []string{"NETWORK_INTERFACE"}, + Destination: &NetworkInterface, + }, + &cli.BoolFlag{ + Name: "ViewPlugins", + Aliases: []string{"vp"}, + Usage: "Shows plugins available to be executed", + EnvVars: []string{"VIEW_PLUGIN"}, + Destination: &ViewPlugin, + }, + &cli.BoolFlag{ + Name: "TrackedContainers", + Aliases: []string{"tc"}, + Usage: "View containers which have " + + "been created from the client side ", + EnvVars: []string{"TRACKED_CONTAINERS"}, + Destination: &TrackedContainers, + }, + &cli.StringFlag{ + Name: "ExecutePlugin", + Aliases: []string{"plugin"}, + Usage: "Plugin which needs to be executed", + EnvVars: []string{"EXECUTE_PLUGIN"}, + Destination: &ExecutePlugin, + }, + &cli.BoolFlag{ + Name: "CreateGroup", + Aliases: []string{"cgroup"}, + Usage: "Creates a new group", + EnvVars: []string{"CREATE_GROUP"}, + Destination: &CreateGroup, + }, + &cli.StringFlag{ + Name: "Group", + Aliases: []string{"group"}, + Usage: "group flag with argument group ID", + EnvVars: []string{"GROUP"}, + Destination: &Group, + }, + &cli.BoolFlag{ + Name: "Groups", + Aliases: []string{"groups"}, + Usage: "View all groups", + EnvVars: []string{"GROUPS"}, + Destination: &Groups, + }, + &cli.BoolFlag{ + Name: "RemoveContainerGroup", + Aliases: []string{"rmcgroup"}, + Usage: "Remove specific container in the group", + EnvVars: []string{"REMOVE_CONTAINER_GROUP"}, + Destination: &RemoveContainerGroup, + }, + &cli.StringFlag{ + Name: "RemoveGroup", + Aliases: []string{"rmgroup"}, + Usage: "Removes the entire group", + EnvVars: []string{"REMOVE_GROUP"}, + Destination: &RemoveGroup, + }, + // Generate only allowed in dev release + // -- REMOVE ON REGULAR RELEASE -- + &cli.StringFlag{ + Name: "Generate", + Aliases: []string{"gen"}, + Usage: "Generates a new copy of P2PRC which can be modified based on your needs", + EnvVars: []string{"GENERATE"}, + Destination: &Generate, + }, + &cli.StringFlag{ + Name: "ModuleName", + Aliases: []string{"mod"}, + Usage: "New go project module name", + EnvVars: []string{"MODULENAME"}, + Destination: &Modulename, + }, + &cli.BoolFlag{ + Name: "FRPServerProxy", + Aliases: []string{"proxy"}, + Usage: "Starts proxy for server", + EnvVars: []string{"FRPSERVERPROXY"}, + Destination: &FRPProxy, + }, + //-------------------------------- + &cli.StringFlag{ + Name: "PullPlugin", + Aliases: []string{"pp"}, + Usage: "Pulls plugin from git repos", + EnvVars: []string{"PULLPLUGIN"}, + Destination: &PullPlugin, + }, + &cli.StringFlag{ + Name: "RemovePlugin", + Aliases: []string{"rp"}, + Usage: "Removes plugin", + EnvVars: []string{"REMOVEPLUGIN"}, + Destination: &RemovePlugin, + }, +} diff --git a/config/config.go b/config/config.go index b465430..6c648e0 100644 --- a/config/config.go +++ b/config/config.go @@ -1,72 +1,73 @@ package config import ( - "github.com/spf13/viper" - "io" - "os" + "github.com/spf13/viper" + "io" + "os" ) var ( - defaultPath string - defaults = map[string]interface{}{} - configName = "config" - configType = "json" - configFile = "config.json" - configPaths []string - defaultEnvName = "P2PRC" + defaultPath string + defaults = map[string]interface{}{} + configName = "config" + configType = "json" + configFile = "config.json" + configPaths []string + defaultEnvName = "P2PRC" ) type Config struct { - IPTable string - DockerContainers string - DefaultDockerFile string - SpeedTestFile string - IPV6Address string - PluginPath string - TrackContainersPath string - ServerPort string - GroupTrackContainersPath string - //NetworkInterface string - //NetworkInterfaceIPV6Index int + IPTable string + DockerContainers string + DefaultDockerFile string + SpeedTestFile string + IPV6Address string + PluginPath string + TrackContainersPath string + ServerPort string + GroupTrackContainersPath string + FRPServerPort string + //NetworkInterface string + //NetworkInterfaceIPV6Index int } // Exists reports whether the named file or directory exists. func fileExists(name string) bool { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - return false - } - } - return true + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true } // Copy the src file to dst. Any existing file will be overwritten and will not // copy file attributes. // Source: https://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file func Copy(src, dst string) error { - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() - out, err := os.Create(dst) - if err != nil { - return err - } - defer out.Close() + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() - _, err = io.Copy(out, in) - if err != nil { - return err - } - return out.Close() + _, err = io.Copy(out, in) + if err != nil { + return err + } + return out.Close() } // GetPathP2PRC Getting P2PRC Directory from environment variable -func GetPathP2PRC()(string,error) { - curDir := os.Getenv(defaultEnvName) - return curDir + "/", nil +func GetPathP2PRC() (string, error) { + curDir := os.Getenv(defaultEnvName) + return curDir + "/", nil } // SetEnvName Sets the environment name @@ -74,113 +75,112 @@ func GetPathP2PRC()(string,error) { // your environment variable // This is useful when extending the use case of P2PRC func SetEnvName(EnvName string) error { - defaultEnvName = EnvName - // Handling error to be implemented only if needed - return nil + defaultEnvName = EnvName + // Handling error to be implemented only if needed + return nil } // GetCurrentPath Getting P2PRC Directory from environment variable -func GetCurrentPath()(string,error) { - curDir := os.Getenv("PWD") - return curDir + "/", nil +func GetCurrentPath() (string, error) { + curDir := os.Getenv("PWD") + return curDir + "/", nil } - // SetDefaults This function to be called only during a // make install func SetDefaults() error { - //Setting current directory to default path - defaultPath, err := GetPathP2PRC() - if err != nil{ - return err - } + //Setting current directory to default path + defaultPath, err := GetPathP2PRC() + if err != nil { + return err + } - //Creates ip_table.json in the json directory - err = Copy("p2p/ip_table.json","p2p/iptable/ip_table.json") - if err != nil { - return err - } + //Creates ip_table.json in the json directory + err = Copy("p2p/ip_table.json", "p2p/iptable/ip_table.json") + if err != nil { + return err + } - //Creates a copy of trackcontainers.json in the appropriate directory - err = Copy("client/trackcontainers.json","client/trackcontainers/trackcontainers.json") - if err != nil { - return err - } + //Creates a copy of trackcontainers.json in the appropriate directory + err = Copy("client/trackcontainers.json", "client/trackcontainers/trackcontainers.json") + if err != nil { + return err + } - //Creates a copy of trackcontainers.json in the appropriate directory - err = Copy("client/grouptrackcontainers.json","client/trackcontainers/grouptrackcontainers.json") - if err != nil { - return err - } + //Creates a copy of trackcontainers.json in the appropriate directory + err = Copy("client/grouptrackcontainers.json", "client/trackcontainers/grouptrackcontainers.json") + if err != nil { + return err + } + //Setting default paths for the config file + defaults["IPTable"] = defaultPath + "p2p/iptable/ip_table.json" + defaults["DefaultDockerFile"] = defaultPath + "server/docker/containers/docker-ubuntu-sshd/" + defaults["DockerContainers"] = defaultPath + "server/docker/containers/" + defaults["SpeedTestFile"] = defaultPath + "p2p/50.bin" + defaults["IPV6Address"] = "" + defaults["PluginPath"] = defaultPath + "plugin/deploy" + defaults["TrackContainersPath"] = defaultPath + "client/trackcontainers/trackcontainers.json" + defaults["GroupTrackContainersPath"] = defaultPath + "client/trackcontainers/grouptrackcontainers.json" + defaults["ServerPort"] = "8088" + defaults["FRPServerPort"] = "0" + //defaults["NetworkInterface"] = "wlp0s20f3" + //defaults["NetworkInterfaceIPV6Index"] = "2" - //Setting default paths for the config file - defaults["IPTable"] = defaultPath + "p2p/iptable/ip_table.json" - defaults["DefaultDockerFile"] = defaultPath + "server/docker/containers/docker-ubuntu-sshd/" - defaults["DockerContainers"] = defaultPath + "server/docker/containers/" - defaults["SpeedTestFile"] = defaultPath + "p2p/50.bin" - defaults["IPV6Address"] = "" - defaults["PluginPath"] = defaultPath + "plugin/deploy" - defaults["TrackContainersPath"] = defaultPath + "client/trackcontainers/trackcontainers.json" - defaults["GroupTrackContainersPath"] = defaultPath + "client/trackcontainers/grouptrackcontainers.json" - defaults["ServerPort"] = "8088" - //defaults["NetworkInterface"] = "wlp0s20f3" - //defaults["NetworkInterfaceIPV6Index"] = "2" + //Paths to search for config file + configPaths = append(configPaths, defaultPath) - //Paths to search for config file - configPaths = append(configPaths, defaultPath) + if fileExists(defaultPath + "config.json") { + err := os.Remove(defaultPath + "config.json") + if err != nil { + return err + } + } - if fileExists(defaultPath + "config.json") { - err := os.Remove(defaultPath + "config.json") - if err != nil { - return err - } - } - - //Calling configuration file - _, err = ConfigInit() - if err != nil { - return err - } - return nil + //Calling configuration file + _, err = ConfigInit() + if err != nil { + return err + } + return nil } -func ConfigInit()(*Config,error) { +func ConfigInit() (*Config, error) { - //Setting current directory to default path - defaultPath, err := GetPathP2PRC() - if err != nil{ - return nil, err - } - //Paths to search for config file - configPaths = append(configPaths, defaultPath) + //Setting current directory to default path + defaultPath, err := GetPathP2PRC() + if err != nil { + return nil, err + } + //Paths to search for config file + configPaths = append(configPaths, defaultPath) - //Add all possible configurations paths - for _,v := range configPaths { - viper.AddConfigPath(v) - } + //Add all possible configurations paths + for _, v := range configPaths { + viper.AddConfigPath(v) + } - //Read config file - if err := viper.ReadInConfig(); err != nil { - // If the error thrown is config file not found - //Sets default configuration to viper - for k,v := range defaults { - viper.SetDefault(k,v) - } - viper.SetConfigName(configName) - viper.SetConfigFile(configFile) - viper.SetConfigType(configType) + //Read config file + if err := viper.ReadInConfig(); err != nil { + // If the error thrown is config file not found + //Sets default configuration to viper + for k, v := range defaults { + viper.SetDefault(k, v) + } + viper.SetConfigName(configName) + viper.SetConfigFile(configFile) + viper.SetConfigType(configType) - if err = viper.WriteConfig(); err != nil { - return nil,err - } - } + if err = viper.WriteConfig(); err != nil { + return nil, err + } + } - // Adds configuration to the struct - var config Config - if err := viper.Unmarshal(&config); err != nil { - return nil,err - } + // Adds configuration to the struct + var config Config + if err := viper.Unmarshal(&config); err != nil { + return nil, err + } - return &config,nil + return &config, nil } diff --git a/p2p/frp/client.go b/p2p/frp/client.go index 09d60db..3bd51b4 100644 --- a/p2p/frp/client.go +++ b/p2p/frp/client.go @@ -1,33 +1,121 @@ package frp import ( + "git.sr.ht/~akilan1999/p2p-rendering-computation/server/docker" "github.com/fatedier/frp/client" "github.com/fatedier/frp/pkg/config" + "math/rand" + "strconv" ) -// Starts FRP client -func StartFRPClient(address string, port int) error { +// Client This struct stores +// client information with server +// proxy connected +type Client struct { + Name string + Server *Server + ClientMappings []ClientMapping +} - cfg := config.GetDefaultClientConf() - //var cfg config.ClientCommonConf - var visitorCfgs map[string]config.VisitorConf - // - cfg.ServerAddr = address - cfg.ServerPort = port +// ClientMapping Stores client mapping ports +// to proxy server +type ClientMapping struct { + LocalIP string + LocalPort int + RemotePort int +} - var tcpcnf config.TCPProxyConf - tcpcnf.LocalIP = "0.0.0.0" - tcpcnf.LocalPort = 22 - tcpcnf.RemotePort = 3000 +// StartFRPClientForServer Starts Server using FRP server +// returns back a port +func StartFRPClientForServer(ipaddress string, port string) (string, error) { + // Setup server information + var s Server + s.address = ipaddress + // convert port to int + portInt, err := strconv.Atoi(port) + if err != nil { + return "", err + } + s.port = portInt - proxyConfs := map[string]config.ProxyConf{ - tcpcnf.ProxyName: &tcpcnf, + // Setup client information + var c Client + c.Name = "ServerPort" + c.Server = &s + + //random port + randPort := rangeIn(10000, 99999) + c.ClientMappings = []ClientMapping{ + { + LocalIP: ipaddress, + LocalPort: randPort, + RemotePort: randPort, + }, } - //cfg.ClientConfig = auth.GetDefaultClientConf() - //cfg.Protocol = "tcp" + // Start client server + go c.StartFRPClient() - //pxyCfgs, visitorCfgs, _ = config.LoadAllProxyConfsFromIni(cfg.User, nil, cfg.Start) + return strconv.Itoa(randPort), nil + +} + +func StartFRPCDockerContainer(ipaddress string, port string, docker *docker.DockerVM) error { + // Setup server information + var s Server + s.address = ipaddress + // convert port to int + portInt, err := strconv.Atoi(port) + if err != nil { + return err + } + s.port = portInt + + // Setup client information + var c Client + c.Name = "ServerPort" + c.Server = &s + + // set client mapping + var clientMappings []ClientMapping + for i, _ := range docker.Ports.PortSet { + // Set client mapping + var clientMapping ClientMapping + portMap := docker.Ports.PortSet[i].ExternalPort + clientMapping.LocalPort = portMap + clientMapping.RemotePort = portMap + // Append to array + clientMappings = append(clientMappings, clientMapping) + } + + // Start client server + go c.StartFRPClient() + + return nil + +} + +// StartFRPClient Starts FRP client +func (c *Client) StartFRPClient() error { + + cfg := config.GetDefaultClientConf() + + var proxyConfs map[string]config.ProxyConf + var visitorCfgs map[string]config.VisitorConf + + proxyConfs = make(map[string]config.ProxyConf) + + cfg.ServerAddr = c.Server.address + cfg.ServerPort = c.Server.port + + for i, _ := range c.ClientMappings { + var tcpcnf config.TCPProxyConf + tcpcnf.LocalIP = c.ClientMappings[i].LocalIP + tcpcnf.LocalPort = c.ClientMappings[i].LocalPort + tcpcnf.RemotePort = c.ClientMappings[i].RemotePort + + proxyConfs[tcpcnf.ProxyName] = &tcpcnf + } cli, err := client.NewService(cfg, proxyConfs, visitorCfgs, "") if err != nil { @@ -38,3 +126,9 @@ func StartFRPClient(address string, port int) error { return nil } + +// helper function to generate random +// number in a certain range +func rangeIn(low, hi int) int { + return low + rand.Intn(hi-low) +} diff --git a/p2p/frp/server.go b/p2p/frp/server.go index d906d10..4c435df 100644 --- a/p2p/frp/server.go +++ b/p2p/frp/server.go @@ -1,21 +1,58 @@ package frp import ( + configP2PRC "git.sr.ht/~akilan1999/p2p-rendering-computation/config" "github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/server" + "strconv" ) -type ServerFRPConf struct { +// TODO: Implement a way to +// check if ports are taken + +type Server struct { address string + port int +} + +// StartFRPProxyFromConfig starts +// reverse proxy server based on +// the port provided in the config file +// field FRPServerPort. +func StartFRPProxyFromConfig() error { + // gets current configuration + config, err := configP2PRC.ConfigInit() + if err != nil { + return err + } + + var s Server + s.address = "0.0.0.0" + // Converting config port to int + port, err := strconv.Atoi(config.FRPServerPort) + if err != nil { + return err + } + s.port = port + + // start FRP server + + err = s.StartFRPServer() + if err != nil { + return err + } + + return nil + } // StartFRPServer The initial plan is only support reverse proxy // for TCP ports // This function starts a server that can act as a reverse // proxy for nodes behind NAT. -func StartFRPServer(address string, port int) error { +func (s *Server) StartFRPServer() error { baseConfig := config.GetDefaultServerConf() - baseConfig.BindAddr = address + baseConfig.BindAddr = s.address //TODO look into later for dashboard //baseConfig.DashboardAddr = "127.0.0.1" @@ -24,7 +61,7 @@ func StartFRPServer(address string, port int) error { //baseConfig.DashboardPwd = "admin" // todo change to make it a dynamic port - baseConfig.BindPort = port + baseConfig.BindPort = s.port service, err := server.NewService(baseConfig) if err != nil { return err diff --git a/p2p/frp/server_test.go b/p2p/frp/server_test.go index 2f41498..04867d1 100644 --- a/p2p/frp/server_test.go +++ b/p2p/frp/server_test.go @@ -6,24 +6,39 @@ import ( "time" ) +// Testing scenario FRPServer func TestStartFRPServer(t *testing.T) { - err := StartFRPServer("127.0.0.1", 8808) + var s Server + s.address = "127.0.0.1" + s.port = 8808 + err := s.StartFRPServer() if err != nil { fmt.Println(err) t.Fail() } } +// Testing scenario FRPServer and FRPClient connection func TestStartFRPClient(t *testing.T) { - go StartFRPServer("127.0.0.1", 8808) - //if err != nil { - // fmt.Println(err) - // t.Fail() - //} + var s Server + s.address = "127.0.0.1" + s.port = 8808 + go s.StartFRPServer() time.Sleep(3 * time.Second) - err := StartFRPClient("127.0.0.1", 8808) + // Sample test client + var c Client + c.Server = &s + c.ClientMappings = []ClientMapping{ + { + LocalIP: "127.0.0.1", + LocalPort: 22, + RemotePort: 3301, + }, + } + + err := c.StartFRPClient() if err != nil { fmt.Println(err) t.Fail() diff --git a/p2p/iptable.go b/p2p/iptable.go index f3eb69a..d9ceea9 100644 --- a/p2p/iptable.go +++ b/p2p/iptable.go @@ -1,238 +1,241 @@ package p2p import ( - "encoding/json" - "fmt" - "git.sr.ht/~akilan1999/p2p-rendering-computation/config" - "io/ioutil" - "net" - "net/http" - "os" - "time" + "encoding/json" + "fmt" + "git.sr.ht/~akilan1999/p2p-rendering-computation/config" + "io/ioutil" + "net" + "net/http" + "os" + "time" ) // Get IP table Data type IpAddresses struct { - IpAddress []IpAddress `json:"ip_address"` + IpAddress []IpAddress `json:"ip_address"` } type IpAddress struct { - Ipv4 string `json:"ipv4"` - Ipv6 string `json:"ipv6"` - Latency time.Duration `json:"latency"` - Download float64 `json:"download"` - Upload float64 `json:"upload"` - ServerPort string `json:"serverport"` + Ipv4 string `json:"ipv4"` + Ipv6 string `json:"ipv6"` + Latency time.Duration `json:"latency"` + Download float64 `json:"download"` + Upload float64 `json:"upload"` + ServerPort string `json:"serverport"` + ProxyPort string `json:"proxyport"` } type IP struct { - Query string + Query string } // ReadIpTable Read data from Ip tables from json file -func ReadIpTable()(*IpAddresses ,error){ +func ReadIpTable() (*IpAddresses, error) { // Get Path from config - config, err := config.ConfigInit() - if err != nil { - return nil,err - } - jsonFile, err := os.Open(config.IPTable) - // if we os.Open returns an error then handle it - if err != nil { - return nil,err - } + config, err := config.ConfigInit() + if err != nil { + return nil, err + } + jsonFile, err := os.Open(config.IPTable) + // if we os.Open returns an error then handle it + if err != nil { + return nil, err + } + // defer the closing of our jsonFile so that we can parse it later on + defer jsonFile.Close() - // defer the closing of our jsonFile so that we can parse it later on - defer jsonFile.Close() + // read our opened xmlFile as a byte array. + byteValue, _ := ioutil.ReadAll(jsonFile) - // read our opened xmlFile as a byte array. - byteValue, _ := ioutil.ReadAll(jsonFile) + // we initialize our Users array + var ipAddresses IpAddresses - // we initialize our Users array - var ipAddresses IpAddresses + // we unmarshal our byteArray which contains our + // jsonFile's content into 'users' which we defined above + json.Unmarshal(byteValue, &ipAddresses) - // we unmarshal our byteArray which contains our - // jsonFile's content into 'users' which we defined above - json.Unmarshal(byteValue, &ipAddresses) + var PublicIP IpAddress - var PublicIP IpAddress + ipv6, err := GetCurrentIPV6() + if err != nil { + return nil, err + } - ipv6, err := GetCurrentIPV6() - if err != nil { - return nil, err - } + ip, err := CurrentPublicIP() + if err != nil { + return nil, err + } + PublicIP.Ipv4 = ip + PublicIP.Ipv6 = ipv6 + PublicIP.ServerPort = config.ServerPort + if config.FRPServerPort != "0" { + PublicIP.ProxyPort = config.FRPServerPort + } - ip, err := CurrentPublicIP() - if err != nil { - return nil, err - } - PublicIP.Ipv4 = ip - PublicIP.Ipv6 = ipv6 - PublicIP.ServerPort = config.ServerPort + // Updates current machine IP address to the IP table + ipAddresses.IpAddress = append(ipAddresses.IpAddress, PublicIP) - // Updates current machine IP address to the IP table - ipAddresses.IpAddress = append(ipAddresses.IpAddress, PublicIP) - - //before writing to iptable ensures the duplicates are removed - if err = ipAddresses.RemoveDuplicates(); err != nil { - return nil, err - } + //before writing to iptable ensures the duplicates are removed + if err = ipAddresses.RemoveDuplicates(); err != nil { + return nil, err + } return &ipAddresses, nil } // WriteIpTable Write to IP table json file func (i *IpAddresses) WriteIpTable() error { - //before writing to iptable ensures the duplicates are removed - if err := i.RemoveDuplicates(); err != nil { - return err - } + //before writing to iptable ensures the duplicates are removed + if err := i.RemoveDuplicates(); err != nil { + return err + } - file, err := json.MarshalIndent(i, "", " ") - if err != nil { - return err - } + file, err := json.MarshalIndent(i, "", " ") + if err != nil { + return err + } - // Get Path from config - config, err := config.ConfigInit() - if err != nil { - return err - } + // Get Path from config + config, err := config.ConfigInit() + if err != nil { + return err + } - err = ioutil.WriteFile(config.IPTable, file, 0644) - if err != nil { - return err - } + err = ioutil.WriteFile(config.IPTable, file, 0644) + if err != nil { + return err + } - return nil + return nil } // PrintIpTable Print Ip table data for Cli func PrintIpTable() error { - table, err := ReadIpTable() + table, err := ReadIpTable() - if err != nil { - return err - } + if err != nil { + return err + } - for i := 0; i < len(table.IpAddress); i++ { - fmt.Printf("\nIP Address: %s\nIPV6: %s\nLatency: %s\nServerPort: %s\n-----------" + - "-----------------\n",table.IpAddress[i].Ipv4,table.IpAddress[i].Ipv6, - table.IpAddress[i].Latency, table.IpAddress[i].ServerPort) - } + for i := 0; i < len(table.IpAddress); i++ { + fmt.Printf("\nIP Address: %s\nIPV6: %s\nLatency: %s\nServerPort: %s\n-----------"+ + "-----------------\n", table.IpAddress[i].Ipv4, table.IpAddress[i].Ipv6, + table.IpAddress[i].Latency, table.IpAddress[i].ServerPort) + } - return nil + return nil } // RemoveDuplicates This is a temporary fix current functions failing to remove // Duplicate IP addresses from local IP table -func (table *IpAddresses)RemoveDuplicates() error { +func (table *IpAddresses) RemoveDuplicates() error { - var NoDuplicates IpAddresses - for i, _:= range table.IpAddress { - Exists := false - for k := range NoDuplicates.IpAddress { - if (NoDuplicates.IpAddress[k].Ipv4 != "" && NoDuplicates.IpAddress[k].Ipv4 == table.IpAddress[i].Ipv4) || (NoDuplicates.IpAddress[k].Ipv6 != "" && NoDuplicates.IpAddress[k].Ipv6 == table.IpAddress[i].Ipv6) { - Exists = true - break - } - } - if Exists { - continue - } - NoDuplicates.IpAddress = append(NoDuplicates.IpAddress, table.IpAddress[i]) - } + var NoDuplicates IpAddresses + for i, _ := range table.IpAddress { + Exists := false + for k := range NoDuplicates.IpAddress { + if (NoDuplicates.IpAddress[k].Ipv4 != "" && NoDuplicates.IpAddress[k].Ipv4 == table.IpAddress[i].Ipv4) || (NoDuplicates.IpAddress[k].Ipv6 != "" && NoDuplicates.IpAddress[k].Ipv6 == table.IpAddress[i].Ipv6) { + Exists = true + break + } + } + if Exists { + continue + } + NoDuplicates.IpAddress = append(NoDuplicates.IpAddress, table.IpAddress[i]) + } - table.IpAddress = NoDuplicates.IpAddress + table.IpAddress = NoDuplicates.IpAddress - return nil + return nil } // CurrentPublicIP Get Current Public IP address -func CurrentPublicIP() (string,error) { - req, err := http.Get("http://ip-api.com/json/") - if err != nil { - return "",err - } - defer req.Body.Close() +func CurrentPublicIP() (string, error) { + req, err := http.Get("http://ip-api.com/json/") + if err != nil { + return "", err + } + defer req.Body.Close() - body, err := ioutil.ReadAll(req.Body) - if err != nil { - return "",err - } + body, err := ioutil.ReadAll(req.Body) + if err != nil { + return "", err + } - var ip IP - json.Unmarshal(body, &ip) + var ip IP + json.Unmarshal(body, &ip) - return ip.Query, nil + return ip.Query, nil } // GetCurrentIPV6 gets the current IPV6 address based on the interface // specified in the config file -func GetCurrentIPV6()(string,error){ - Config, err := config.ConfigInit() - if err != nil { - return "",err - } +func GetCurrentIPV6() (string, error) { + Config, err := config.ConfigInit() + if err != nil { + return "", err + } - // Fix in future release - //byNameInterface, err := net.InterfaceByName(Config.NetworkInterface) - //if err != nil { - // return "",err - //} - //addresses, err := byNameInterface.Addrs() - //if err != nil { - // return "",err - //} - //if addresses[1].String() == "" { - // return "",errors.New("IPV6 address not detected") - //} - //IP,_,err := net.ParseCIDR(addresses[Config.NetworkInterfaceIPV6Index].String()) - //if err != nil { - // return "",err - //} + // Fix in future release + //byNameInterface, err := net.InterfaceByName(Config.NetworkInterface) + //if err != nil { + // return "",err + //} + //addresses, err := byNameInterface.Addrs() + //if err != nil { + // return "",err + //} + //if addresses[1].String() == "" { + // return "",errors.New("IPV6 address not detected") + //} + //IP,_,err := net.ParseCIDR(addresses[Config.NetworkInterfaceIPV6Index].String()) + //if err != nil { + // return "",err + //} - return Config.IPV6Address, nil + return Config.IPV6Address, nil } // ViewNetworkInterface This function is created to view the network interfaces available func ViewNetworkInterface() error { - ifaces, err := net.Interfaces() - if err != nil { - return err - } - for _, i := range ifaces { - addrs, err := i.Addrs() - if err != nil { - return err - } - for index, a := range addrs { - switch v := a.(type) { - case *net.IPAddr: - fmt.Printf("(%v) %v : %s (%s)\n", index, i.Name, v, v.IP.DefaultMask()) + ifaces, err := net.Interfaces() + if err != nil { + return err + } + for _, i := range ifaces { + addrs, err := i.Addrs() + if err != nil { + return err + } + for index, a := range addrs { + switch v := a.(type) { + case *net.IPAddr: + fmt.Printf("(%v) %v : %s (%s)\n", index, i.Name, v, v.IP.DefaultMask()) - case *net.IPNet: - fmt.Printf("(%v) %v : %s \n", index, i.Name, v) - } + case *net.IPNet: + fmt.Printf("(%v) %v : %s \n", index, i.Name, v) + } - } - } - return nil + } + } + return nil } // Ip4or6 Helper function to check if the IP address is IPV4 or //IPV6 (https://socketloop.com/tutorials/golang-check-if-ip-address-is-version-4-or-6) func Ip4or6(s string) string { - for i := 0; i < len(s); i++ { - switch s[i] { - case '.': - return "version 4" - case ':': - return "version 6" - } - } - return "version 6" + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return "version 4" + case ':': + return "version 6" + } + } + return "version 6" -} \ No newline at end of file +} diff --git a/server/server.go b/server/server.go index 45552c5..fe58c5b 100644 --- a/server/server.go +++ b/server/server.go @@ -1,147 +1,208 @@ package server import ( - "encoding/json" - "fmt" - "git.sr.ht/~akilan1999/p2p-rendering-computation/config" - "git.sr.ht/~akilan1999/p2p-rendering-computation/p2p" - "git.sr.ht/~akilan1999/p2p-rendering-computation/server/docker" - "github.com/gin-gonic/gin" - "io/ioutil" - "net/http" + "encoding/json" + "fmt" + "git.sr.ht/~akilan1999/p2p-rendering-computation/config" + "git.sr.ht/~akilan1999/p2p-rendering-computation/p2p" + "git.sr.ht/~akilan1999/p2p-rendering-computation/p2p/frp" + "git.sr.ht/~akilan1999/p2p-rendering-computation/server/docker" + "github.com/gin-gonic/gin" + "io/ioutil" + "net/http" ) -func Server() error{ - r := gin.Default() +func Server() error { + r := gin.Default() - // Gets default information of the server - r.GET("/server_info", func(c *gin.Context) { - c.JSON(http.StatusOK, ServerInfo()) - }) + // update IPTable with new port and ip address and update ip table + var ProxyIpAddr p2p.IpAddress - // Speed test with 50 mbps - r.GET("/50", func(c *gin.Context){ - // Get Path from config - config, err := config.ConfigInit() - if err != nil { - c.String(http.StatusOK, fmt.Sprint(err)) - } - c.File(config.SpeedTestFile) - }) + // Gets default information of the server + r.GET("/server_info", func(c *gin.Context) { + c.JSON(http.StatusOK, ServerInfo()) + }) - // Route build to do a speed test - r.GET("/upload", func(c *gin.Context) { - file, _ := c.FormFile("file") + // Speed test with 50 mbps + r.GET("/50", func(c *gin.Context) { + // Get Path from config + config, err := config.ConfigInit() + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } + c.File(config.SpeedTestFile) + }) - // Upload the file to specific dst. - // c.SaveUploadedFile(file, dst) + // Route build to do a speed test + r.GET("/upload", func(c *gin.Context) { + file, _ := c.FormFile("file") - c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) - }) + // Upload the file to specific dst. + // c.SaveUploadedFile(file, dst) - //Gets Ip Table from server node - r.POST("/IpTable", func(c *gin.Context) { - // Getting IPV4 address of client - var ClientHost p2p.IpAddress + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) + }) - if p2p.Ip4or6(c.ClientIP()) == "version 6" { - ClientHost.Ipv6 = c.ClientIP() - } else { - ClientHost.Ipv4 = c.ClientIP() - } + //Gets Ip Table from server node + r.POST("/IpTable", func(c *gin.Context) { + // Getting IPV4 address of client + var ClientHost p2p.IpAddress - // Variable to store IP table information - var IPTable p2p.IpAddresses + if p2p.Ip4or6(c.ClientIP()) == "version 6" { + ClientHost.Ipv6 = c.ClientIP() + } else { + ClientHost.Ipv4 = c.ClientIP() + } - // Receive file from POST request - body, err := c.FormFile("json") - if err != nil { - c.String(http.StatusOK, fmt.Sprint(err)) - } + // Variable to store IP table information + var IPTable p2p.IpAddresses - // Open file - open, err := body.Open() - if err != nil { - c.String(http.StatusOK, fmt.Sprint(err)) - } + // Receive file from POST request + body, err := c.FormFile("json") + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } - // Open received file - file, err := ioutil.ReadAll(open) - if err != nil { - c.String(http.StatusOK, fmt.Sprint(err)) - } + // Open file + open, err := body.Open() + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } - json.Unmarshal(file,&IPTable) + // Open received file + file, err := ioutil.ReadAll(open) + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } - //Add Client IP address to IPTable struct - IPTable.IpAddress = append(IPTable.IpAddress, ClientHost) + json.Unmarshal(file, &IPTable) - // Runs speed test to return only servers in the IP table pingable - err = IPTable.SpeedTestUpdatedIPTable() - if err != nil { - c.String(http.StatusOK, fmt.Sprint(err)) - } + //Add Client IP address to IPTable struct + IPTable.IpAddress = append(IPTable.IpAddress, ClientHost) - // Reads IP addresses from ip table - IpAddresses,err := p2p.ReadIpTable() - if err != nil { - c.String(http.StatusOK, fmt.Sprint(err)) - } + // Runs speed test to return only servers in the IP table pingable + err = IPTable.SpeedTestUpdatedIPTable() + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } - c.JSON(http.StatusOK, IpAddresses) - }) + // Reads IP addresses from ip table + IpAddresses, err := p2p.ReadIpTable() + if err != nil { + c.String(http.StatusOK, fmt.Sprint(err)) + } + + c.JSON(http.StatusOK, IpAddresses) + }) // Starts docker container in server - r.GET("/startcontainer", func(c *gin.Context) { - // Get Number of ports to open and whether to use GPU or not - Ports := c.DefaultQuery("ports","0") - GPU := c.DefaultQuery("GPU","false") - ContainerName := c.DefaultQuery("ContainerName","") + r.GET("/startcontainer", func(c *gin.Context) { + // Get Number of ports to open and whether to use GPU or not + Ports := c.DefaultQuery("ports", "0") + GPU := c.DefaultQuery("GPU", "false") + ContainerName := c.DefaultQuery("ContainerName", "") var PortsInt int - // Convert Get Request value to int - fmt.Sscanf(Ports, "%d", &PortsInt) + // Convert Get Request value to int + fmt.Sscanf(Ports, "%d", &PortsInt) - // Creates container and returns-back result to - // access container - resp, err := docker.BuildRunContainer(PortsInt,GPU,ContainerName) + // Creates container and returns-back result to + // access container + resp, err := docker.BuildRunContainer(PortsInt, GPU, ContainerName) - if err != nil { - c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) - } + if ProxyIpAddr.Ipv4 != "" { + err := frp.StartFRPCDockerContainer(ProxyIpAddr.Ipv4, ProxyIpAddr.ProxyPort, resp) + if err != nil { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } + } - c.JSON(http.StatusOK, resp) - }) + if err != nil { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } - //Remove container - r.GET("/RemoveContainer", func(c *gin.Context) { - ID := c.DefaultQuery("id","0") - if err := docker.StopAndRemoveContainer(ID); err != nil { - c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) - } - c.String(http.StatusOK, "success") - }) + c.JSON(http.StatusOK, resp) + }) - //Show images available - r.GET("/ShowImages", func(c *gin.Context) { - resp, err := docker.ViewAllContainers() - if err != nil { - c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) - } - c.JSON(http.StatusOK, resp) - }) + //Remove container + r.GET("/RemoveContainer", func(c *gin.Context) { + ID := c.DefaultQuery("id", "0") + if err := docker.StopAndRemoveContainer(ID); err != nil { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } + c.String(http.StatusOK, "success") + }) - //Get Server port based on the config file - config, err := config.ConfigInit() - if err != nil { - return err - } + //Show images available + r.GET("/ShowImages", func(c *gin.Context) { + resp, err := docker.ViewAllContainers() + if err != nil { + c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err)) + } + c.JSON(http.StatusOK, resp) + }) - // Run gin server on the specified port - err = r.Run(":" + config.ServerPort) - if err != nil { - return err - } + //Get Server port based on the config file + config, err := config.ConfigInit() + if err != nil { + return err + } - return nil + // If there is a proxy port specified + // then starts the FRP server + if config.FRPServerPort != "0" { + go frp.StartFRPProxyFromConfig() + } + + // TODO check if IPV6 or Proxy port is specified + // if not update current entry as proxy address + // with appropriate port on IP Table + if config.IPV6Address == "" || config.FRPServerPort == "0" { + table, err := p2p.ReadIpTable() + if err != nil { + return err + } + + var lowestLatency int64 + // random large number + lowestLatency = 10000000 + var lowestLatencyIpAddress p2p.IpAddress + for i, _ := range table.IpAddress { + // Checks if the ping is the lowest and if the following node is acting as a proxy + if table.IpAddress[i].Latency.Milliseconds() < lowestLatency && table.IpAddress[i].ProxyPort != "" { + lowestLatency = table.IpAddress[i].Latency.Milliseconds() + lowestLatencyIpAddress = table.IpAddress[i] + } + } + + // If there is an identified node + if lowestLatency != 10000000 { + // Starts FRP as a client with + proxyPort, err := frp.StartFRPClientForServer(lowestLatencyIpAddress.Ipv4, lowestLatencyIpAddress.ProxyPort) + if err != nil { + return err + } + + // updating with the current proxy address + ProxyIpAddr.Ipv4 = lowestLatencyIpAddress.Ipv4 + ProxyIpAddr.ServerPort = proxyPort + ProxyIpAddr.ProxyPort = lowestLatencyIpAddress.ProxyPort + + // append the following to the ip table + table.IpAddress = append(table.IpAddress, ProxyIpAddr) + // write information back to the IP Table + table.WriteIpTable() + // update ip table + go table.SpeedTestUpdatedIPTable() + } + + } + + // Run gin server on the specified port + err = r.Run(":" + config.ServerPort) + if err != nil { + return err + } + + return nil }