Skip to content

Commit

Permalink
started with a needed refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
realDragonium committed Jul 19, 2021
1 parent 633d54d commit 0acc267
Show file tree
Hide file tree
Showing 27 changed files with 2,644 additions and 633 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

# Test binary, built with `go test -c`
*.test
*.profile

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

Ultraviolet.*
# Dependency directories (remove the comment below to include it)
# vendor/
20 changes: 6 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Ultraviolet
# Ultraviolet - Alpha
## Notes
- Tableflip has been removed and all related features

## What is it
Like [Infrared](https://github.com/haveachin/infrared), Ultraviolet is an ultra lightweight Minecraft reverse proxy written in Go. Not even sure or this will be a production ready software some day, its mostly a different structure I want to try and see what kind of effect it has on performance and such. It should work most of the time, although there isnt much code/features dedicated to prevent some mistakes from happening or which can recover when certain errors occur.

Expand All @@ -7,13 +10,12 @@ Like [Infrared](https://github.com/haveachin/infrared), Ultraviolet is an ultra
### How to build
Ultraviolet can be ran by using docker or you can also build a binary yourself by running:
```
go build -tags netgo
go build
```

### Features
[x] HAProxy protocol(v2) support (sending only)
[x] RealIP (v2.4&v2.5)
[x] Can restart without shutting down open connections (check [Tableflip](#tableflip))
[x] Rate limiting
[x] Status caching (online status only)
[x] Offline status placeholder
Expand All @@ -23,20 +25,10 @@ go build -tags netgo
### Im not gonna fool proof this
Im not planning on writing code to prevent Ultraviolet from crashing if you did something stupid. If the config wants a timeout time and you put in a negative number that may or may not cause some issues and that is your own fault.

### Tableflip
This has implemented [tableflip](https://github.com/cloudflare/tableflip) which should make it able to reload/restart Ultraviolet without closing existing connections on Linux and macOS. Ultraviolet should still be usable on windows (testing purposes only pls).
Check their [documentation](https://pkg.go.dev/github.com/cloudflare/tableflip) to know what or how.

IMPORTANT: There is a limit of one 'parent' process. So when you reload Ultraviolet once you need to wait until the parent process is closed (all previous connections have been closed) before you can reload it again.

## Command-Line Flags
`-pid-file` specifies the path of the pid file ultraviolet will use [default: `"/run/ultraviolet.pid"`]

`-config` specifies the path to the main config [default: `"/etc/ultraviolet/ultraviolet.json"`]

`-config` specifies the path to the main config [default: `"/etc/ultraviolet/ultraviolet.json"`]
`-server-configs` specifies the path to all your server configs [default: `"/etc/ultraviolet/config/"`]


## How does some stuff work
### rate limiting
With rate limiting Ultraviolet will allow a specific number of connections to be made to the backend within a given time frame. It will reset when the time frame `rateCooldown` has passed. When the number has been exceeded but the cooldown isnt over yet, Ultraviolet will behave like the server is offline.
Expand Down
73 changes: 73 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package config

import (
"crypto/ecdsa"
"time"

"github.com/realDragonium/Ultraviolet/mc"
)

type ServerState byte

const (
UNKNOWN ServerState = iota
ONLINE
OFFLINE
UPDATE
)

func (state ServerState) String() string {
var text string
switch state {
case UNKNOWN:
text = "Unknown"
case ONLINE:
text = "Online"
case OFFLINE:
text = "Offline"
case UPDATE:
text = "Update"
}
return text
}

type ServerConfigFile struct {
}

func (file ServerConfigFile) ReadConfig() {

}

type RateLimitConfig struct {
rateCounter int
rateStartTime time.Time
rateLimit int
rateLimitStatus bool
rateCooldown time.Duration
}

type StateConfig struct {
state ServerState
stateCooldown time.Duration
stateUpdateCh chan ServerState
}

type OldRealIPConfig struct {
}

type NewRealIPConfig struct {
keyPath string
realIPKey *ecdsa.PrivateKey
}

type OfflineStatusConfig struct {
offlineStatus mc.Packet
}

type CacheStatusConfig struct {
cachedStatus mc.Packet
statusCooldown time.Duration
statusCacheTime time.Time
statusLatency time.Duration
statusHandshake mc.Packet
}
66 changes: 66 additions & 0 deletions config/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,69 @@ func generateKeys(cfg ServerConfig) *ecdsa.PrivateKey {
}
return privkey
}

func FileToWorkerConfig2(cfg ServerConfig) (WorkerServerConfig2, error) {
var privateKey *ecdsa.PrivateKey
if cfg.NewRealIP {
var err error
privateKey, err = ReadPrivateKey(cfg.RealIPKey)
if errors.Is(err, os.ErrNotExist) {
//Check or there is already one generate or save the generated path
// TODO: IMPROVE THIS
if key, ok := existingGeneratedKey(cfg); ok {
privateKey = key
} else {
log.Printf("No existing key for %s has been found, generating one...", cfg.Domains[0])
privateKey = generateKeys(cfg)
}
} else if err != nil {
log.Printf("error during reading of private key: %v", err)
}
}
disconPk := mc.ClientBoundDisconnect{
Reason: mc.Chat(cfg.DisconnectMessage),
}.Marshal()
offlineStatusPk := cfg.OfflineStatus.Marshal()
duration, _ := time.ParseDuration(cfg.RateDuration)
if duration == 0 {
duration = time.Second
}
cooldown, _ := time.ParseDuration(cfg.StateUpdateCooldown)
if cooldown == 0 {
cooldown = time.Second
}
dialTimeout, _ := time.ParseDuration(cfg.DialTimeout)
if dialTimeout == 0 {
dialTimeout = time.Second
}
cacheCooldown, _ := time.ParseDuration(cfg.CacheUpdateCooldown)
if cacheCooldown == 0 {
cacheCooldown = time.Second
}
offlineBytes, err := offlineStatusPk.Marshal()
if err != nil {
return WorkerServerConfig2{}, err
}
disconBytes, err := disconPk.Marshal()
if err != nil {
return WorkerServerConfig2{}, err
}
return WorkerServerConfig2{
ProxyTo: cfg.ProxyTo,
ProxyBind: cfg.ProxyBind,
DialTimeout: dialTimeout,
SendProxyProtocol: cfg.SendProxyProtocol,
CacheStatus: cfg.CacheStatus,
ValidProtocol: cfg.ValidProtocol,
CacheUpdateCooldown: cacheCooldown,
OfflineStatus: offlineBytes,
DisconnectPacket: disconBytes,
RateLimit: cfg.RateLimit,
RateLimitStatus: cfg.RateLimitStatus,
RateLimitDuration: duration,
StateUpdateCooldown: cooldown,
OldRealIp: cfg.OldRealIP,
NewRealIP: cfg.NewRealIP,
RealIPKey: privateKey,
}, nil
}
20 changes: 20 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,23 @@ type WorkerServerConfig struct {
RateLimitStatus bool
RateLimitDuration time.Duration
}

type WorkerServerConfig2 struct {
StateUpdateCooldown time.Duration
OldRealIp bool
NewRealIP bool
RealIPKey *ecdsa.PrivateKey
CacheStatus bool
CacheUpdateCooldown time.Duration
ValidProtocol int
OfflineStatus []byte
DisconnectPacket []byte
ProxyTo string
ProxyBind string
DialTimeout time.Duration
SendProxyProtocol bool
RateLimit int
RateLimitStatus bool
RateLimitDuration time.Duration
}

10 changes: 0 additions & 10 deletions examples/ultraviolet.service.example

This file was deleted.

5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@ module github.com/realDragonium/Ultraviolet

go 1.16

require (
github.com/cloudflare/tableflip v1.2.2
github.com/pires/go-proxyproto v0.5.0
)
require github.com/pires/go-proxyproto v0.5.0
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
github.com/cloudflare/tableflip v1.2.2 h1:WkhiowHlg0nZuH7Y2beLVIZDfxtSvKta1f22PEgUN7w=
github.com/cloudflare/tableflip v1.2.2/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E=
github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo=
github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
47 changes: 8 additions & 39 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@ package main

import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"net"
"path/filepath"
"syscall"

"github.com/cloudflare/tableflip"
"github.com/realDragonium/Ultraviolet/config"
"github.com/realDragonium/Ultraviolet/proxy"
"github.com/realDragonium/Ultraviolet/old_proxy"
)

var (
Expand All @@ -22,9 +18,8 @@ var (
)

func main() {
log.Printf("Starting up Alpha-v0.%d", 7)
log.Printf("Starting up Alpha-v0.%d", 9)
var (
pidFile = flag.String("pid-file", "/run/ultraviolet.pid", "`Path` to pid file")
mainCfgPath = flag.String("config", defaultUltravioletCfgPath, "`Path` to main config file")
serverCfgsPath = flag.String("server-configs", defaultServerCfgPath, "`Path` to server config files")
)
Expand All @@ -38,42 +33,16 @@ func main() {
if err != nil {
log.Fatalf("Something went wrong while reading config files: %v", err)
}
reqCh := make(chan proxy.McRequest)
gateway := proxy.NewGateway()
reqCh := make(chan old_proxy.McRequest)
gateway := old_proxy.NewGateway()
gateway.StartWorkers(mainCfg, serverCfgs, reqCh)

log.SetPrefix(fmt.Sprintf("%d ", os.Getpid()))
upg, err := tableflip.New(tableflip.Options{
PIDFile: *pidFile,
})
if err != nil {
panic(err)
}
defer upg.Stop()
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGHUP)
for range sig {
err := upg.Upgrade()
if err != nil {
log.Println("upgrade failed:", err)
}
}
}()

ln, err := upg.Listen("tcp", mainCfg.ListenTo)
ln, err := net.Listen("tcp", mainCfg.ListenTo)
if err != nil {
log.Fatalf("Can't listen: %v", err)
}
defer ln.Close()
go proxy.ServeListener(ln, reqCh)
go old_proxy.ServeListener(ln, reqCh)

log.Printf("Finished starting up")
if err := upg.Ready(); err != nil {
panic(err)
}
<-upg.Exit()
log.Println("Waiting for all open connections to close before shutting down")
gateway.Shutdown()
log.Println("Shutting down")
select {}
}
Loading

0 comments on commit 0acc267

Please sign in to comment.