diff --git a/README.md b/README.md index b6156de..50af6b9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ -# darkstorm-server -Experimenting with a Go server for personal uses +# Darkstorm Server + +Experimenting with a Go server for personal uses. Combines a simple website server with a tcp forwarder. + +Configure which ports go to which addresses via /etc/darkstorm-server.conf in the form `port address`. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fe43ed8 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/CalebQ42/darkstorm-server + +go 1.17 diff --git a/main.go b/main.go new file mode 100644 index 0000000..f5cf822 --- /dev/null +++ b/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "log" + "time" +) + +var quitChan chan string = make(chan string) + +func main() { + go tcpLinker() + go webserver() + for failure := <-quitChan; ; failure = <-quitChan { + switch failure { + case "tcp conf": + continue + case "tcp err": + go tcpLinkerRestart() + case "web arg": + continue + case "web err": + go websiteRestart() + } + } +} + +func tcpLinkerRestart() { + log.Println("TCP linker failed. Restarting in 5 seconds...") + time.Sleep(5 * time.Second) + log.Println("Restarting tcp linker") + tcpLinker() +} + +func websiteRestart() { + log.Println("Website failed. Restarting in 5 seconds...") + time.Sleep(5 * time.Second) + log.Println("Restarting website") + webserver() +} diff --git a/tcp.go b/tcp.go new file mode 100644 index 0000000..a8b44a0 --- /dev/null +++ b/tcp.go @@ -0,0 +1,136 @@ +package main + +import ( + "bufio" + "errors" + "io" + "log" + "net" + "os" + "strconv" + "strings" + "time" +) + +func tcpLinker() { + links, err := parseConf() + if links == nil { + log.Println("No values in config file or file not present (/etc/darkstorm-server.conf). tcp linker signing off") + quitChan <- "tcp conf" + return + } else if err != nil { + log.Println("Error while trying to parse config file:", err) + quitChan <- "tcp conf" + return + } + fails := make(map[int]int) //logs how many fails per 5 seconds + failChan := make(chan int, 20) + open := make(map[int]bool) + for port, addr := range links { + open[port] = true + go link(port, addr, failChan) + } +failWaiting: + for portFail := <-failChan; ; portFail = <-failChan { + if fails[portFail] == 0 { + go func() { + time.Sleep(5 * time.Second) + fails[portFail] = 0 + }() + } else if fails[portFail] == 4 { + log.Println("Port", portFail, "has failed 5 time is as many seconds. Not restarting port...") + open[portFail] = false + for _, b := range open { + if b { + continue failWaiting + } + } + log.Println("All ports dead. Attempting restart...") + quitChan <- "tcp err" + return + } + fails[portFail]++ + log.Println("Restarting linking for port", portFail) + go link(portFail, links[portFail], failChan) + } + +} + +func link(port int, addr string, failChan chan int) { + listen, err := net.Listen("tcp", ":"+strconv.Itoa(port)) + defer listen.Close() + if err != nil { + log.Println("Error while trying to listen to port ", port, ":", err) + failChan <- port + return + } + for { + con, err := listen.Accept() + defer con.Close() + if err != nil { + log.Println("Error while trying to accept connection to port ", port, ":", err) + failChan <- port + return + } + ext, err := net.Dial("tcp", addr) + defer ext.Close() + if err != nil { + log.Println("Error while trying to dial", addr, ":", err) + failChan <- port + return + } + _, err = io.Copy(ext, con) + if err != nil { + log.Println("Error while trying to copy data to", addr, ":", err) + failChan <- port + return + } + _, err = io.Copy(con, ext) + if err != nil { + log.Println("Error while trying to copy data to port", port, ":", err) + failChan <- port + return + } + } +} + +func parseConf() (links map[int]string, err error) { + conf, err := os.Open("/etc/darkstorm-server.conf") + if os.IsNotExist(err) { + return nil, nil + } + lineNum := 0 + links = make(map[int]string) + rdr := bufio.NewReader(conf) + for { + lineNum++ + var origLine string + origLine, err = rdr.ReadString('\n') + if err != nil { + break + } + line := strings.ReplaceAll(origLine, "\t", " ") + for strings.Contains(line, " ") { + line = strings.Replace(line, " ", " ", -1) + } + line = strings.TrimSpace(line) + if line == "" { + continue + } + split := strings.Split(line, " ") + if len(split) != 2 { + return nil, errors.New("invalid line #" + strconv.Itoa(lineNum)) + } + var i int + i, err = strconv.Atoi(split[0]) + if err != nil { + return nil, errors.New("invalid line #" + strconv.Itoa(lineNum)) + } + links[i] = split[1] + } + err = nil + if len(links) > 0 { + return nil, nil + } + return +} diff --git a/web.go b/web.go new file mode 100644 index 0000000..da88db9 --- /dev/null +++ b/web.go @@ -0,0 +1,26 @@ +package main + +import ( + "flag" + "log" + "net/http" +) + +func webserver() { + flag.Parse() + path := flag.Arg(0) + keyPath := flag.Arg(0) + if path == "" { + log.Println("No argument given for website file path. website signing off...") + quitChan <- "web arg" + return + } else if keyPath == "" { + log.Println("No argument given for key files. website signing off...") + quitChan <- "web arg" + return + } + http.Handle("/", http.FileServer(http.Dir(path))) + err := http.ListenAndServeTLS(":443", keyPath+"/cert.pem", keyPath+"/key.pem", nil) + log.Println("Error while serving website:", err) + quitChan <- "web err" +}