mirror of
https://github.com/PretendoNetwork/nex-go.git
synced 2025-04-02 11:02:14 -04:00
230 lines
5.6 KiB
Go
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
|
|
}
|