Skip to content

Commit

Permalink
Implement joinserver.jsp and checkserver.jsp
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-goode committed Aug 15, 2024
1 parent a249e96 commit 716737c
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 50 deletions.
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,16 +219,22 @@ func (app *App) MakeServer() *echo.Echo {

// Session
sessionHasJoined := SessionHasJoined(app)
sessionCheckServer := SessionCheckServer(app)
sessionJoin := SessionJoin(app)
sessionJoinServer := SessionJoinServer(app)
sessionProfile := SessionProfile(app)
sessionBlockedServers := SessionBlockedServers(app)
e.GET("/session/minecraft/hasJoined", sessionHasJoined)
e.GET("/game/checkserver.jsp", sessionCheckServer)
e.POST("/session/minecraft/join", sessionJoin)
e.POST("/game/joinserver.jsp", sessionJoinServer)
e.GET("/session/minecraft/profile/:id", sessionProfile)
e.GET("/blockedservers", sessionBlockedServers)

e.GET("/session/session/minecraft/hasJoined", sessionHasJoined)
e.GET("/session/game/checkserver.jsp", sessionCheckServer)
e.POST("/session/session/minecraft/join", sessionJoin)
e.POST("/session/game/joinserver.jsp", sessionJoinServer)
e.GET("/session/session/minecraft/profile/:id", sessionProfile)
e.GET("/session/blockedservers", sessionBlockedServers)

Expand Down
171 changes: 121 additions & 50 deletions session.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"net/http"
"net/url"
"strings"
)

type sessionJoinRequest struct {
Expand Down Expand Up @@ -41,6 +42,59 @@ func SessionJoin(app *App) func(c echo.Context) error {
}
}

// /game/joinserver.jsp
func SessionJoinServer(app *App) func(c echo.Context) error {
return func(c echo.Context) error {
username := c.QueryParam("user")
sessionID := c.QueryParam("sessionId")
serverID := c.QueryParam("serverId")

// If any parameters are missing, return NO
if username == "" || sessionID == "" || serverID == "" {
return c.String(http.StatusMethodNotAllowed, "NO")
}

// Parse sessionId. It has the form:
// token:<accessToken>:<player UUID>
split := strings.Split(sessionID, ":")
if len(split) != 3 || split[0] != "token" {
return c.String(http.StatusMethodNotAllowed, "NO")
}
accessToken := split[1]
id := split[2]

// Is the accessToken valid?
client := app.GetClient(accessToken, StalePolicyDeny)
if client == nil {
return c.String(http.StatusMethodNotAllowed, "NO")
}

// If the player name corresponding to the access token doesn't match
// the `user` param from the request, return NO
user := client.User
if user.PlayerName != username {
return c.String(http.StatusMethodNotAllowed, "NO")
}
// If the player's UUID doesn't match the UUID in the sessionId, return
// NO
userID, err := UUIDToID(user.UUID)
if err != nil {
return err
}
if userID != id {
return c.String(http.StatusMethodNotAllowed, "NO")
}

user.ServerID = MakeNullString(&serverID)
result := app.DB.Save(&user)
if result.Error != nil {
return result.Error
}

return c.String(http.StatusOK, "YES")
}
}

func fullProfile(app *App, user *User, uuid string, sign bool) (SessionProfileResponse, error) {
id, err := UUIDToID(uuid)
if err != nil {
Expand All @@ -59,67 +113,84 @@ func fullProfile(app *App, user *User, uuid string, sign bool) (SessionProfileRe
}, nil
}

// /session/minecraft/hasJoined
// https://c4k3.github.io/wiki.vg/Protocol_Encryption.html#Server
func SessionHasJoined(app *App) func(c echo.Context) error {
return func(c echo.Context) error {
playerName := c.QueryParam("username")
serverID := c.QueryParam("serverId")
func (app *App) hasJoined(c *echo.Context, playerName string, serverID string, legacy bool) error {
var user User
result := app.DB.First(&user, "player_name = ?", playerName)
// If the error isn't "not found", throw.
if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
return result.Error
}

var user User
result := app.DB.First(&user, "player_name = ?", playerName)
if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
if app.Config.TransientUsers.Allow && app.TransientUsernameRegex.MatchString(playerName) {
var err error
user, err = MakeTransientUser(app, playerName)
if err != nil {
return err
}
} else {
return c.NoContent(http.StatusForbidden)
if result.Error != nil || !user.ServerID.Valid || serverID != user.ServerID.String {
for _, fallbackAPIServer := range app.Config.FallbackAPIServers {
if fallbackAPIServer.DenyUnknownUsers && result.Error != nil {
// If DenyUnknownUsers is enabled and the player name is
// not known, don't query the fallback server.
continue
}
base, err := url.Parse(fallbackAPIServer.SessionURL)
if err != nil {
log.Println(err)
continue
}
}

if result.Error != nil || !user.ServerID.Valid || serverID != user.ServerID.String {
for _, fallbackAPIServer := range app.Config.FallbackAPIServers {
if fallbackAPIServer.DenyUnknownUsers && result.Error != nil {
// If DenyUnknownUsers is enabled and the player name is
// not known, don't query the fallback server.
continue
}
base, err := url.Parse(fallbackAPIServer.SessionURL)
if err != nil {
log.Println(err)
continue
}

base.Path += "/session/minecraft/hasJoined"
params := url.Values{}
params.Add("username", playerName)
params.Add("serverId", serverID)
base.RawQuery = params.Encode()
base.Path += "/session/minecraft/hasJoined"
params := url.Values{}
params.Add("username", playerName)
params.Add("serverId", serverID)
base.RawQuery = params.Encode()

res, err := MakeHTTPClient().Get(base.String())
if err != nil {
log.Printf("Received invalid response from fallback API server at %s\n", base.String())
continue
}
defer res.Body.Close()
res, err := MakeHTTPClient().Get(base.String())
if err != nil {
log.Printf("Received invalid response from fallback API server at %s\n", base.String())
continue
}
defer res.Body.Close()

if res.StatusCode == http.StatusOK {
return c.Stream(http.StatusOK, res.Header.Get("Content-Type"), res.Body)
if res.StatusCode == http.StatusOK {
if legacy {
return (*c).String(http.StatusOK, "YES")
} else {
return (*c).Stream(http.StatusOK, res.Header.Get("Content-Type"), res.Body)
}
}

return c.NoContent(http.StatusForbidden)
}

profile, err := fullProfile(app, &user, user.UUID, true)
if err != nil {
return err
if legacy {
return (*c).String(http.StatusMethodNotAllowed, "NO")
} else {
return (*c).NoContent(http.StatusForbidden)
}
}

return c.JSON(http.StatusOK, profile)
if legacy {
return (*c).String(http.StatusOK, "YES")
}

profile, err := fullProfile(app, &user, user.UUID, true)
if err != nil {
return err
}

return (*c).JSON(http.StatusOK, profile)
}

// /session/minecraft/hasJoined
// https://c4k3.github.io/wiki.vg/Protocol_Encryption.html#Server
func SessionHasJoined(app *App) func(c echo.Context) error {
return func(c echo.Context) error {
playerName := c.QueryParam("username")
serverID := c.QueryParam("serverId")
return app.hasJoined(&c, playerName, serverID, false)
}
}

// /game/checkserver.jsp
func SessionCheckServer(app *App) func(c echo.Context) error {
return func(c echo.Context) error {
playerName := c.QueryParam("username")
serverID := c.QueryParam("serverId")
return app.hasJoined(&c, playerName, serverID, true)
}
}

Expand Down

0 comments on commit 716737c

Please sign in to comment.