commit b4e2e4a52a285f9b128d0c5463a1fa0484e5a531 Author: Christian Barth Date: Sun Apr 29 01:25:38 2018 +0200 Inititial commit diff --git a/core/communications.go b/core/communications.go new file mode 100644 index 0000000..54f6aca --- /dev/null +++ b/core/communications.go @@ -0,0 +1,32 @@ +package core + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// SendObject sends a object through the writer +func SendObject(writer *http.ResponseWriter, data interface{}) { + js := ToJSON(data) + if js == "" { + SendJSON(writer, "{\"server_message\":\"internal_sending_error\"") + return + } + SendJSON(writer, js) +} + +// SendJSON sends JSON string through the writer +func SendJSON(writer *http.ResponseWriter, data string) { + (*writer).Header().Set("Content-Type", "application/json") + fmt.Fprintf((*writer), data) +} + +// ToJSON converts an object to a JSON string +func ToJSON(in interface{}) string { + data, err := json.Marshal(in) + if err != nil { + return "" + } + return string(data) +} diff --git a/gamemanagment.go b/gamemanagment.go new file mode 100644 index 0000000..78b0fbb --- /dev/null +++ b/gamemanagment.go @@ -0,0 +1,92 @@ +package main + +import ( + "bytes" + "encoding/json" + "net/http" + "werwolf/core" +) + +var games []JSON_Game + +type JSON_Game struct { + Token string `json:"token"` + Username string `json:"username"` + Gamename string `json:"gamename"` +} + +// getGame returns a reference to a game-Object identified by token +func getGame(token string) (*JSON_Game, bool) { + for _, g := range games { + if g.Token == token { + return &g, true + } + } + return nil, false +} + +// addGame adds a game-Object game to the list of active games or replaces an inactive game. +func addGame(game JSON_Game) bool { + a := true + for _, g := range games { + if g.Token == "" { + g = game + a = false + return true + } + } + if a { + games = append(games, game) + } + return true +} + +// removeGame disables a game-Object by setting its token to none. Object to be edited is identified by token. Returns true if successfull or false if not found. +func removeGame(token string) bool { + for _, g := range games { + if g.Token == token { + g.Token = "" + return true + } + } + return false +} + +func newGame(responseWriter http.ResponseWriter, request *http.Request) { + var game = new(JSON_Game) + buf := new(bytes.Buffer) + buf.ReadFrom(request.Body) + + if json.Unmarshal(buf.Bytes(), game) == nil { + game.Token = randomString(32) + if addGame(*game) { + core.SendObject(&responseWriter, game) + addGame(*game) + } else { + sendError(&responseWriter, "game_creation") + } + } else { + sendError(&responseWriter, "json") + } +} + +func joinGame(responseWriter http.ResponseWriter, request *http.Request) { + var game = new(JSON_Game) + buf := new(bytes.Buffer) + buf.ReadFrom(request.Body) + + if json.Unmarshal(buf.Bytes(), game) == nil { + game, succ := getGame(game.Token) + if succ { + core.SendObject(&responseWriter, *game) + } else { + sendError(&responseWriter, "game_notfound") + } + } else { + sendError(&responseWriter, "json") + } +} + +func sendError(responseWriter *http.ResponseWriter, errorMsg string) { + core.SendObject(responseWriter, map[string]string{"state": errorMsg + "_error"}) +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..61555f2 --- /dev/null +++ b/index.html @@ -0,0 +1,31 @@ + + + Werwolf Test page + + + + +
+ +
+
+
+ + + +
+
+ + + \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..5249310 --- /dev/null +++ b/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/rs/cors" +) + +func main() { + fmt.Println("Starting Werwolf Server") + mux := http.NewServeMux() + + mux.HandleFunc("/werwolf/creategame", http.HandlerFunc(newGame)) + mux.HandleFunc("/werwolf/joingame", http.HandlerFunc(joinGame)) + + corsHandler := cors.Default().Handler(mux) + http.ListenAndServe(":25566", corsHandler) +} diff --git a/randomstring.go b/randomstring.go new file mode 100644 index 0000000..4a21c84 --- /dev/null +++ b/randomstring.go @@ -0,0 +1,32 @@ +package main + +import ( + "math/rand" + "time" +) + +const ( + letterIdxBits = 6 + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + return string(b) +} diff --git a/realip/realip.go b/realip/realip.go new file mode 100644 index 0000000..e2803a2 --- /dev/null +++ b/realip/realip.go @@ -0,0 +1,89 @@ +package realip + +import ( + "errors" + "net" + "net/http" + "strings" +) + +var cidrs []*net.IPNet + +func init() { + maxCidrBlocks := []string{ + "127.0.0.1/8", // localhost + "10.0.0.0/8", // 24-bit block + "172.16.0.0/12", // 20-bit block + "192.168.0.0/16", // 16-bit block + "169.254.0.0/16", // link local address + "::1/128", // localhost IPv6 + "fc00::/7", // unique local address IPv6 + "fe80::/10", // link local address IPv6 + } + + cidrs = make([]*net.IPNet, len(maxCidrBlocks)) + for i, maxCidrBlock := range maxCidrBlocks { + _, cidr, _ := net.ParseCIDR(maxCidrBlock) + cidrs[i] = cidr + } +} + +// isLocalAddress works by checking if the address is under private CIDR blocks. +// List of private CIDR blocks can be seen on : +// +// https://en.wikipedia.org/wiki/Private_network +// +// https://en.wikipedia.org/wiki/Link-local_address +func isPrivateAddress(address string) (bool, error) { + ipAddress := net.ParseIP(address) + if ipAddress == nil { + return false, errors.New("address is not valid") + } + + for i := range cidrs { + if cidrs[i].Contains(ipAddress) { + return true, nil + } + } + + return false, nil +} + +// FromRequest return client's real public IP address from http request headers. +func FromRequest(r *http.Request) string { + // Fetch header value + xRealIP := r.Header.Get("X-Real-Ip") + xForwardedFor := r.Header.Get("X-Forwarded-For") + + // If both empty, return IP from remote address + if xRealIP == "" && xForwardedFor == "" { + var remoteIP string + + // If there are colon in remote address, remove the port number + // otherwise, return remote address as is + if strings.ContainsRune(r.RemoteAddr, ':') { + remoteIP, _, _ = net.SplitHostPort(r.RemoteAddr) + } else { + remoteIP = r.RemoteAddr + } + + return remoteIP + } + + // Check list of IP in X-Forwarded-For and return the first global address + for _, address := range strings.Split(xForwardedFor, ",") { + address = strings.TrimSpace(address) + isPrivate, err := isPrivateAddress(address) + if !isPrivate && err == nil { + return address + } + } + + // If nothing succeed, return X-Real-IP + return xRealIP +} + +// RealIP is depreciated, use FromRequest instead +func RealIP(r *http.Request) string { + return FromRequest(r) +} diff --git a/werwolf b/werwolf new file mode 100755 index 0000000..c821475 Binary files /dev/null and b/werwolf differ