Skip to content

Commit

Permalink
feat: Validate URLs on per-game basis
Browse files Browse the repository at this point in the history
  • Loading branch information
cetteup committed Jul 28, 2022
1 parent 1feeff7 commit 3240fe2
Show file tree
Hide file tree
Showing 18 changed files with 89 additions and 31 deletions.
2 changes: 2 additions & 0 deletions game/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type Config struct {
AdditionalProcessNames map[string]bool
}

type URLValidator func(u *url.URL) error

type CommandBuilder func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error)

func PrepareLaunch(config Config) error {
Expand Down
7 changes: 3 additions & 4 deletions game/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,8 @@ func (r GameRouter) StartGame(commandLineUrl string) error {
return fmt.Errorf("game not supported: %s", u.Scheme)
}

port := u.Port()
if gameTitle.RequiresPort && port == "" {
return fmt.Errorf("port is required but was not given in URL")
if err = gameTitle.URLValidator(u); err != nil {
return err
}

// Build final launcher config
Expand All @@ -227,7 +226,7 @@ func (r GameRouter) StartGame(commandLineUrl string) error {
if err = launcher.PrepareLaunch(gameTitle.LauncherConfig); err != nil {
return err
}
if err = launcher.StartGame(launcherConfig, gameTitle.CmdBuilder, u.Scheme, u.Hostname(), port, u); err != nil {
if err = launcher.StartGame(launcherConfig, gameTitle.CmdBuilder, u.Scheme, u.Hostname(), u.Port(), u); err != nil {
return err
}

Expand Down
2 changes: 1 addition & 1 deletion game/title/title.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
type GameTitle struct {
ProtocolScheme string
GameLabel string
RequiresPort bool
PlatformClient *platform.Client
FinderConfigs []finder.Config
LauncherConfig launcher.Config
URLValidator launcher.URLValidator
CmdBuilder launcher.CommandBuilder
}

Expand Down
35 changes: 34 additions & 1 deletion game/titles/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,47 @@ package titles
import (
"fmt"
"net/url"
"regexp"

"github.com/cetteup/joinme.click-launcher/game/launcher"
)

const (
urlQueryKeyMod = "mod"
// game ids vary by length, so for now we are just validating that it only contains numbers
frostbite3GameIdPattern = `^\d+$`
urlQueryKeyMod = "mod"
)

var ipPortURLValidator launcher.URLValidator = func(u *url.URL) error {
hostname, port := u.Hostname(), u.Port()
if !isValidIPv4(hostname) {
return fmt.Errorf("url hostname is not a valid IPv4 address: %s", hostname)
}
if port == "" {
return fmt.Errorf("port is missing from url")
}
// When parsing a URL, only the port format is validated (numbers only)
// The url package does not ensure that a port is within the valid TCP/UDP port range, so we need to take care of that
if !isValidPort(port) {
return fmt.Errorf("url port is not a valid network port: %s", port)
}

return nil
}

var frostbite3GameIdURLValidator launcher.URLValidator = func(u *url.URL) error {
hostname := u.Hostname()
matched, err := regexp.Match(frostbite3GameIdPattern, []byte(hostname))
if err != nil {
return fmt.Errorf("failed to validate game id: %s", err)
}
if !matched {
return fmt.Errorf("url hostname is not a valid game id: %s", hostname)
}

return nil
}

var frostbite3DefaultArgs = []string{
"-gameMode", "MP",
"-role", "soldier",
Expand Down
1 change: 1 addition & 0 deletions game/titles/t_bf1.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var Bf1 = title.GameTitle{
ExecutableName: "bf1.exe",
CloseBeforeLaunch: true,
},
URLValidator: frostbite3GameIdURLValidator,
CmdBuilder: func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
offerIDs := []string{"1026023"}
args := append(frostbite3DefaultArgs, "-gameId", host)
Expand Down
12 changes: 6 additions & 6 deletions game/titles/t_bf1942.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const (
var Bf1942 = title.GameTitle{
ProtocolScheme: "bf1942",
GameLabel: "Battlefield 1942",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -34,7 +33,8 @@ var Bf1942 = title.GameTitle{
ExecutableName: "BF1942.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: bf1942CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: bf1942CmdBuilder,
}

var bf1942CmdBuilder launcher.CommandBuilder = func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
Expand Down Expand Up @@ -64,7 +64,6 @@ var bf1942CmdBuilder launcher.CommandBuilder = func(installPath string, scheme s
var Bf1942RoadToRome = title.GameTitle{
ProtocolScheme: "bf1942rtr",
GameLabel: "Battlefield 1942: The Road to Rome",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -80,13 +79,13 @@ var Bf1942RoadToRome = title.GameTitle{
ExecutableName: "BF1942.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: bf1942CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: bf1942CmdBuilder,
}

var Bf1942SecretWeaponsOfWW2 = title.GameTitle{
ProtocolScheme: "bf1942sw",
GameLabel: "Battlefield 1942: Secret Weapons of WWII",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -102,5 +101,6 @@ var Bf1942SecretWeaponsOfWW2 = title.GameTitle{
ExecutableName: "BF1942.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: bf1942CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: bf1942CmdBuilder,
}
7 changes: 4 additions & 3 deletions game/titles/t_bf2.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const (
var Bf2 = title.GameTitle{
ProtocolScheme: "bf2",
GameLabel: "Battlefield 2",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -37,7 +36,8 @@ var Bf2 = title.GameTitle{
ExecutableName: "BF2.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: bf2CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: bf2CmdBuilder,
}

var bf2CmdBuilder launcher.CommandBuilder = func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
Expand Down Expand Up @@ -99,5 +99,6 @@ var Bf2SF = title.GameTitle{
ExecutableName: "BF2.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: bf2CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: bf2CmdBuilder,
}
1 change: 1 addition & 0 deletions game/titles/t_bf4.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var Bf4 = title.GameTitle{
ExecutableName: "bf4.exe",
CloseBeforeLaunch: true,
},
URLValidator: frostbite3GameIdURLValidator,
CmdBuilder: func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
offerIDs := []string{"1007968", "1011575", "1011576", "1011577", "1010268", "1010269", "1010270", "1010271", "1010958", "1010959", "1010960", "1010961", "1007077", "1016751", "1016757", "1016754", "1015365", "1015364", "1015363", "1015362"}
args := append(frostbite3DefaultArgs, "-gameId", host)
Expand Down
2 changes: 1 addition & 1 deletion game/titles/t_bfvietnam.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const (
var BfVietnam = title.GameTitle{
ProtocolScheme: "bfvietnam",
GameLabel: "Battlefield Vietnam",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -29,6 +28,7 @@ var BfVietnam = title.GameTitle{
ExecutableName: "BfVietnam.exe",
CloseBeforeLaunch: true,
},
URLValidator: ipPortURLValidator,
CmdBuilder: func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
args := []string{
"+joinServer", host,
Expand Down
7 changes: 4 additions & 3 deletions game/titles/t_cod.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
var Cod = title.GameTitle{
ProtocolScheme: "cod",
GameLabel: "Call of Duty",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -21,7 +20,8 @@ var Cod = title.GameTitle{
ExecutableName: "CoDMP.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: plusConnectCmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: plusConnectCmdBuilder,
}

var CodUO = title.GameTitle{
Expand All @@ -38,5 +38,6 @@ var CodUO = title.GameTitle{
ExecutableName: "CoDUOMP.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: plusConnectCmdBuilder,
URLValidator: frostbite3GameIdURLValidator,
CmdBuilder: plusConnectCmdBuilder,
}
4 changes: 2 additions & 2 deletions game/titles/t_cod2.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
var Cod2 = title.GameTitle{
ProtocolScheme: "cod2",
GameLabel: "Call of Duty 2",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -21,5 +20,6 @@ var Cod2 = title.GameTitle{
ExecutableName: "CoD2MP_s.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: plusConnectCmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: plusConnectCmdBuilder,
}
4 changes: 2 additions & 2 deletions game/titles/t_cod4.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
var Cod4 = title.GameTitle{
ProtocolScheme: "cod4",
GameLabel: "Call of Duty 4: Modern Warfare",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -21,5 +20,6 @@ var Cod4 = title.GameTitle{
ExecutableName: "iw3mp.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: plusConnectCmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: plusConnectCmdBuilder,
}
4 changes: 2 additions & 2 deletions game/titles/t_codwaw.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
var CodWaw = title.GameTitle{
ProtocolScheme: "codwaw",
GameLabel: "Call of Duty: World at War",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -21,5 +20,6 @@ var CodWaw = title.GameTitle{
ExecutableName: "CoDWaWmp.exe",
CloseBeforeLaunch: true,
},
CmdBuilder: plusConnectCmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: plusConnectCmdBuilder,
}
2 changes: 1 addition & 1 deletion game/titles/t_fear_sec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
var FearSec2 = title.GameTitle{
ProtocolScheme: "fearsec2",
GameLabel: "F.E.A.R. Combat (SEC2)",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -24,6 +23,7 @@ var FearSec2 = title.GameTitle{
ExecutableName: "FEARMP.exe",
CloseBeforeLaunch: true,
},
URLValidator: ipPortURLValidator,
CmdBuilder: func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
return []string{"+join", fmt.Sprintf("%s:%s", host, port)}, nil
},
Expand Down
2 changes: 1 addition & 1 deletion game/titles/t_paraworld.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const (
var Paraworld = title.GameTitle{
ProtocolScheme: "paraworld",
GameLabel: "ParaWorld",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -35,6 +34,7 @@ var Paraworld = title.GameTitle{
"PWServer.exe": true,
},
},
URLValidator: ipPortURLValidator,
CmdBuilder: func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
args := []string{"-autoconnect", fmt.Sprintf("%s:%s", host, port)}
query := u.Query()
Expand Down
7 changes: 4 additions & 3 deletions game/titles/t_swat4.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
var Swat4 = title.GameTitle{
ProtocolScheme: "swat4",
GameLabel: "SWAT 4",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -31,7 +30,8 @@ var Swat4 = title.GameTitle{
StartIn: launcher.BinaryDir,
CloseBeforeLaunch: true,
},
CmdBuilder: swat4CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: swat4CmdBuilder,
}

var swat4CmdBuilder launcher.CommandBuilder = func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
Expand Down Expand Up @@ -59,5 +59,6 @@ var Swat4X = title.GameTitle{
StartIn: launcher.BinaryDir,
CloseBeforeLaunch: true,
},
CmdBuilder: swat4CmdBuilder,
URLValidator: ipPortURLValidator,
CmdBuilder: swat4CmdBuilder,
}
2 changes: 1 addition & 1 deletion game/titles/t_vietcong.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
var Vietcong = title.GameTitle{
ProtocolScheme: "vietcong",
GameLabel: "Vietcong",
RequiresPort: true,
FinderConfigs: []finder.Config{
{
ForType: finder.RegistryFinder,
Expand All @@ -23,6 +22,7 @@ var Vietcong = title.GameTitle{
ExecutableName: "vietcong.exe",
CloseBeforeLaunch: true,
},
URLValidator: ipPortURLValidator,
CmdBuilder: func(installPath string, scheme string, host string, port string, u *url.URL) ([]string, error) {
return []string{"-ip", host, "-port", port}, nil
},
Expand Down
19 changes: 19 additions & 0 deletions game/titles/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package titles
import (
"encoding/hex"
"fmt"
"net"
"net/url"
"os"
"path/filepath"
Expand Down Expand Up @@ -32,6 +33,8 @@ const (
ProfilePasswordConKey = "LocalProfile.setPassword"
// ProfileNumberMaxLength BF2 only uses 4 digit profile numbers
ProfileNumberMaxLength = 4
portMin = 1
portMax = 65535
)

var (
Expand Down Expand Up @@ -239,3 +242,19 @@ func getValidMod(installPath string, modBasePath string, givenMod string, suppor

return mod, nil
}

func isValidIPv4(input string) bool {
ip := net.ParseIP(input)
if ip == nil {
return false
}
return ip.To4() != nil
}

func isValidPort(input string) bool {
portAsInt, err := strconv.ParseInt(input, 10, 32)
if err != nil {
return false
}
return portAsInt >= portMin && portAsInt <= portMax
}

0 comments on commit 3240fe2

Please sign in to comment.