nex-go/hpp_server.go
2024-02-18 22:35:08 -05:00

230 lines
5.6 KiB
Go

package nex
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"strconv"
"github.com/PretendoNetwork/nex-go/types"
)
// HPPServer represents a bare-bones HPP server
type HPPServer struct {
server *http.Server
accessKey string
libraryVersions *LibraryVersions
dataHandlers []func(packet PacketInterface)
errorEventHandlers []func(err *Error)
byteStreamSettings *ByteStreamSettings
AccountDetailsByPID func(pid *types.PID) (*Account, *Error)
AccountDetailsByUsername func(username string) (*Account, *Error)
useVerboseRMC bool
}
// RegisterServiceProtocol registers a NEX service with the HPP server
func (s *HPPServer) RegisterServiceProtocol(protocol ServiceProtocol) {
protocol.SetEndpoint(s)
s.OnData(protocol.HandlePacket)
}
// OnData adds an event handler which is fired when a new HPP request is received
func (s *HPPServer) OnData(handler func(packet PacketInterface)) {
s.dataHandlers = append(s.dataHandlers, handler)
}
// EmitError calls all the endpoints error event handlers with the provided error
func (s *HPPServer) EmitError(err *Error) {
for _, handler := range s.errorEventHandlers {
go handler(err)
}
}
func (s *HPPServer) handleRequest(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
w.WriteHeader(http.StatusBadRequest)
return
}
pidValue := req.Header.Get("pid")
if pidValue == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
// * The server checks that the header exists, but doesn't verify the value
token := req.Header.Get("token")
if token == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
accessKeySignature := req.Header.Get("signature1")
if accessKeySignature == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
passwordSignature := req.Header.Get("signature2")
if passwordSignature == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
pid, err := strconv.Atoi(pidValue)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
rmcRequestString := req.FormValue("file")
rmcRequestBytes := []byte(rmcRequestString)
tcpAddr, err := net.ResolveTCPAddr("tcp", req.RemoteAddr)
if err != nil {
// * Should never happen?
logger.Error(err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}
client := NewHPPClient(tcpAddr, s)
client.SetPID(types.NewPID(uint64(pid)))
hppPacket, err := NewHPPPacket(client, rmcRequestBytes)
if err != nil {
logger.Error(err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}
err = hppPacket.validateAccessKeySignature(accessKeySignature)
if err != nil {
logger.Error(err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}
err = hppPacket.validatePasswordSignature(passwordSignature)
if err != nil {
logger.Error(err.Error())
rmcMessage := hppPacket.RMCMessage()
// HPP returns PythonCore::ValidationError if password is missing or invalid
errorResponse := NewRMCError(s, ResultCodes.PythonCore.ValidationError)
errorResponse.CallID = rmcMessage.CallID
errorResponse.IsHPP = true
_, err = w.Write(errorResponse.Bytes())
if err != nil {
logger.Error(err.Error())
}
return
}
for _, dataHandler := range s.dataHandlers {
go dataHandler(hppPacket)
}
<-hppPacket.processed
if len(hppPacket.payload) > 0 {
_, err = w.Write(hppPacket.payload)
if err != nil {
logger.Error(err.Error())
}
}
}
// Listen starts a HPP server on a given port
func (s *HPPServer) Listen(port int) {
s.server.Addr = fmt.Sprintf(":%d", port)
err := s.server.ListenAndServe()
if err != nil {
panic(err)
}
}
// ListenSecure starts a HPP server on a given port using a secure (TLS) server
func (s *HPPServer) ListenSecure(port int, certFile, keyFile string) {
s.server.Addr = fmt.Sprintf(":%d", port)
err := s.server.ListenAndServeTLS(certFile, keyFile)
if err != nil {
panic(err)
}
}
// Send sends the packet to the packets sender
func (s *HPPServer) Send(packet PacketInterface) {
if packet, ok := packet.(*HPPPacket); ok {
packet.message.IsHPP = true
packet.payload = packet.message.Bytes()
packet.processed <- true
}
}
// LibraryVersions returns the versions that the server has
func (s *HPPServer) LibraryVersions() *LibraryVersions {
return s.libraryVersions
}
// AccessKey returns the servers sandbox access key
func (s *HPPServer) AccessKey() string {
return s.accessKey
}
// SetAccessKey sets the servers sandbox access key
func (s *HPPServer) SetAccessKey(accessKey string) {
s.accessKey = accessKey
}
// ByteStreamSettings returns the settings to be used for ByteStreams
func (s *HPPServer) ByteStreamSettings() *ByteStreamSettings {
return s.byteStreamSettings
}
// SetByteStreamSettings sets the settings to be used for ByteStreams
func (s *HPPServer) SetByteStreamSettings(byteStreamSettings *ByteStreamSettings) {
s.byteStreamSettings = byteStreamSettings
}
// UseVerboseRMC checks whether or not the endpoint uses verbose RMC
func (s *HPPServer) UseVerboseRMC() bool {
return s.useVerboseRMC
}
// EnableVerboseRMC enable or disables the use of verbose RMC
func (s *HPPServer) EnableVerboseRMC(enable bool) {
s.useVerboseRMC = enable
}
// NewHPPServer returns a new HPP server
func NewHPPServer() *HPPServer {
s := &HPPServer{
dataHandlers: make([]func(packet PacketInterface), 0),
errorEventHandlers: make([]func(err *Error), 0),
libraryVersions: NewLibraryVersions(),
byteStreamSettings: NewByteStreamSettings(),
}
mux := http.NewServeMux()
mux.HandleFunc("/hpp/", s.handleRequest)
httpServer := &http.Server{
Handler: mux,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS11, // * The 3DS and Wii U only support up to TLS 1.1 natively
},
}
s.server = httpServer
return s
}