Commit ef89c4a8 authored by Chunchi Che's avatar Chunchi Che Committed by GitHub

Merge pull request #9 from DarkNeos/dev

Dev
parents 4d91f8af bb7863da
...@@ -10,13 +10,14 @@ import ( ...@@ -10,13 +10,14 @@ import (
"unicode/utf16" "unicode/utf16"
"github.com/sktt1ryze/ygopro-proxy/DarkNeos/ygopropb" "github.com/sktt1ryze/ygopro-proxy/DarkNeos/ygopropb"
util "github.com/sktt1ryze/ygopro-proxy/util"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
const FILLING_TOKEN uint16 = 0xcccc const FILLING_TOKEN uint16 = 0xcccc
const UTF16_BUFFER_MAX_LEN int = 20 const UTF16_BUFFER_MAX_LEN int = 20
const PACKET_MIN_LEN int = 3 const PACKET_MIN_LEN int = 3
const COMPONENT = "[transform]"
const ( const (
ProtobufToRawBuf = 1 ProtobufToRawBuf = 1
RawBufToProtobuf = 2 RawBufToProtobuf = 2
...@@ -26,8 +27,9 @@ const ( ...@@ -26,8 +27,9 @@ const (
CtosProtoPlayerInfo = 16 CtosProtoPlayerInfo = 16
CtosProtoJoinGame = 18 CtosProtoJoinGame = 18
StocJoinGame = 18 StocJoinGame = 18
StocChat = 25 StocChat = 25
StocHsPlayerEnter = 32
) )
type YgoPacket struct { type YgoPacket struct {
...@@ -64,7 +66,7 @@ func packetToBuffer(pkt YgoPacket) []byte { ...@@ -64,7 +66,7 @@ func packetToBuffer(pkt YgoPacket) []byte {
return buf return buf
} }
func bufferToPacket(p []byte) (YgoPacket, error) { func bufferToPacket(p []byte, ctx *util.Context) (YgoPacket, error) {
if len(p) < PACKET_MIN_LEN { if len(p) < PACKET_MIN_LEN {
return YgoPacket{}, errors.New(fmt.Sprintf("Packet len too short, len=%d", len(p))) return YgoPacket{}, errors.New(fmt.Sprintf("Packet len too short, len=%d", len(p)))
} }
...@@ -72,6 +74,17 @@ func bufferToPacket(p []byte) (YgoPacket, error) { ...@@ -72,6 +74,17 @@ func bufferToPacket(p []byte) (YgoPacket, error) {
// todo: impl Reader/Writer for buffer // todo: impl Reader/Writer for buffer
packet_len := binary.LittleEndian.Uint16(p) packet_len := binary.LittleEndian.Uint16(p)
proto := p[2] proto := p[2]
if len(p) < int(packet_len+2) {
log.Printf(COMPONENT+
`Unmatched packet size, proto=%d, buffer_length=%d, packet_len=%d, infa_read_len=%d
Use the buffer length.\n`,
proto, len(p), packet_len, ctx.InfaReadBufferLen,
)
packet_len = uint16(len(p) - 2)
}
exdata := p[3 : packet_len+2] exdata := p[3 : packet_len+2]
return YgoPacket{ return YgoPacket{
...@@ -81,7 +94,7 @@ func bufferToPacket(p []byte) (YgoPacket, error) { ...@@ -81,7 +94,7 @@ func bufferToPacket(p []byte) (YgoPacket, error) {
}, nil }, nil
} }
func Transform(src []byte, tranformType int) ([]byte, error) { func Transform(src []byte, tranformType int, ctx *util.Context) ([]byte, error) {
if tranformType == ProtobufToRawBuf { if tranformType == ProtobufToRawBuf {
message := &ygopropb.YgoCtosMsg{} message := &ygopropb.YgoCtosMsg{}
err := proto.Unmarshal(src, message) err := proto.Unmarshal(src, message)
...@@ -96,13 +109,13 @@ func Transform(src []byte, tranformType int) ([]byte, error) { ...@@ -96,13 +109,13 @@ func Transform(src []byte, tranformType int) ([]byte, error) {
case *(ygopropb.YgoCtosMsg_CtosJoinGame): case *(ygopropb.YgoCtosMsg_CtosJoinGame):
packet = (*pCtosJoinGame)(message.GetCtosJoinGame()).Pb2Packet() packet = (*pCtosJoinGame)(message.GetCtosJoinGame()).Pb2Packet()
default: default:
return nil, errors.New("Unhandled YgoCtosMsg type") return nil, errors.New(COMPONENT + "Unhandled YgoCtosMsg type")
} }
return packetToBuffer(packet), nil return packetToBuffer(packet), nil
} else if tranformType == RawBufToProtobuf { } else if tranformType == RawBufToProtobuf {
packet, err := bufferToPacket(src) packet, err := bufferToPacket(src, ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -113,13 +126,15 @@ func Transform(src []byte, tranformType int) ([]byte, error) { ...@@ -113,13 +126,15 @@ func Transform(src []byte, tranformType int) ([]byte, error) {
pb = pStocChat{}.Packet2Pb(packet) pb = pStocChat{}.Packet2Pb(packet)
case StocJoinGame: case StocJoinGame:
pb = pStocJoinGame{}.Packet2Pb(packet) pb = pStocJoinGame{}.Packet2Pb(packet)
case StocHsPlayerEnter:
pb = pStocHsPlayerEnter{}.Packet2Pb(packet)
default: default:
return nil, errors.New(fmt.Sprintf("Unhandled YgoStocMsg type, proto=%d", packet.Proto)) return nil, errors.New(fmt.Sprintf(COMPONENT+"Unhandled YgoStocMsg type, proto=%d", packet.Proto))
} }
return proto.Marshal(&pb) return proto.Marshal(&pb)
} else { } else {
return nil, errors.New("Unknown tranformType") return nil, errors.New(COMPONENT + "Unknown tranformType")
} }
} }
...@@ -233,6 +248,25 @@ func (_ pStocJoinGame) Packet2Pb(pkt YgoPacket) ygopropb.YgoStocMsg { ...@@ -233,6 +248,25 @@ func (_ pStocJoinGame) Packet2Pb(pkt YgoPacket) ygopropb.YgoStocMsg {
} }
} }
type pStocHsPlayerEnter struct{}
func (_ pStocHsPlayerEnter) Packet2Pb(pkt YgoPacket) ygopropb.YgoStocMsg {
name_max := UTF16_BUFFER_MAX_LEN * 2
name := utf16BufferToStr(pkt.Exdata[:name_max])
pos := pkt.Exdata[name_max]
msg := ygopropb.YgoStocMsg_StocHsPlayerEnter{
StocHsPlayerEnter: &ygopropb.StocHsPlayerEnter{
Name: name,
Pos: int32(pos),
},
}
return ygopropb.YgoStocMsg{
Msg: &msg,
}
}
// +++++ Util Functions +++++ // +++++ Util Functions +++++
func strToUtf16Buffer(s string) []uint16 { func strToUtf16Buffer(s string) []uint16 {
......
package main package main
import ( import (
"bufio" "encoding/binary"
"io" "io"
"log" "log"
"net" "net"
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
darkneos "github.com/sktt1ryze/ygopro-proxy/DarkNeos" darkneos "github.com/sktt1ryze/ygopro-proxy/DarkNeos"
util "github.com/sktt1ryze/ygopro-proxy/util"
) )
const TARGET_PORT = ":8000" const TARGET_PORT = ":8000"
...@@ -17,14 +18,19 @@ const PROXY_PORT = ":3344" ...@@ -17,14 +18,19 @@ const PROXY_PORT = ":3344"
const CHANNEL_SIZE = 0x1000 const CHANNEL_SIZE = 0x1000
const BUFFER_SIZE = 0x1000 const BUFFER_SIZE = 0x1000
const TIME_OUT = 5 const TIME_OUT = 5
const COMPONENT = "[proxy]"
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
ReadBufferSize: 0x1000, ReadBufferSize: 0x1000,
WriteBufferSize: 0x1000, WriteBufferSize: 0x1000,
} }
// todo: create Context
func ygoEndpoint(w http.ResponseWriter, r *http.Request) { func ygoEndpoint(w http.ResponseWriter, r *http.Request) {
defer log.Println("ygoEndpoint finished") defer log.Println(COMPONENT + "ygoEndpoint finished")
ctx := util.NewContext()
upgrader.CheckOrigin = wsChecker upgrader.CheckOrigin = wsChecker
...@@ -32,15 +38,17 @@ func ygoEndpoint(w http.ResponseWriter, r *http.Request) { ...@@ -32,15 +38,17 @@ func ygoEndpoint(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
ctx.WsConnected = true
log.Println("Connection to ws://localhost" + TARGET_PORT + " [websocket] succeeded!") log.Println(COMPONENT + "Connection to ws://localhost" + TARGET_PORT + " [websocket] succeeded!")
tcp, err := net.Dial("tcp", "127.0.0.1"+PROXY_PORT) tcp, err := net.Dial("tcp", "127.0.0.1"+PROXY_PORT)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
ctx.TcpConnected = true
log.Println("Connection to " + "12.0.0.1" + PROXY_PORT + " [tcp] succeeded!") log.Println(COMPONENT + "Connection to " + "12.0.0.1" + PROXY_PORT + " [tcp] succeeded!")
wsCh := make(chan []byte, CHANNEL_SIZE) wsCh := make(chan []byte, CHANNEL_SIZE)
tcpCh := make(chan []byte, CHANNEL_SIZE) tcpCh := make(chan []byte, CHANNEL_SIZE)
...@@ -55,8 +63,8 @@ func ygoEndpoint(w http.ResponseWriter, r *http.Request) { ...@@ -55,8 +63,8 @@ func ygoEndpoint(w http.ResponseWriter, r *http.Request) {
close(tcpStopCh) close(tcpStopCh)
}() }()
go wsProxy(ws, wsCh, wsStopCh) go wsProxy(ws, wsCh, wsStopCh, ctx)
go tcpProxy(tcp, tcpCh, tcpStopCh) go tcpProxy(tcp, tcpCh, tcpStopCh, ctx)
for { for {
select { select {
...@@ -82,15 +90,15 @@ func ygoEndpoint(w http.ResponseWriter, r *http.Request) { ...@@ -82,15 +90,15 @@ func ygoEndpoint(w http.ResponseWriter, r *http.Request) {
} }
} }
// todo: generic // todo: use interface
func wsProxy(ws *websocket.Conn, Ch chan<- []byte, stopCh <-chan bool) { func wsProxy(ws *websocket.Conn, Ch chan<- []byte, stopCh <-chan bool, ctx util.Context) {
defer ws.Close() defer ws.Close()
defer close(Ch) defer close(Ch)
for { for {
select { select {
case _, ok := <-stopCh: case _, ok := <-stopCh:
log.Println("wsProxy recv stop singal, exit. channel closed: ", ok) log.Println(COMPONENT+"wsProxy recv stop singal, exit. channel closed: ", ok)
return return
default: default:
// if err := ws.SetReadDeadline(time.Now().Add(time.Second * TIME_OUT)); err != nil { // if err := ws.SetReadDeadline(time.Now().Add(time.Second * TIME_OUT)); err != nil {
...@@ -107,13 +115,14 @@ func wsProxy(ws *websocket.Conn, Ch chan<- []byte, stopCh <-chan bool) { ...@@ -107,13 +115,14 @@ func wsProxy(ws *websocket.Conn, Ch chan<- []byte, stopCh <-chan bool) {
log.Println(err) log.Println(err)
return return
} }
ctx.InfaReadBufferLen = len(buffer)
if messageType == websocket.CloseMessage { if messageType == websocket.CloseMessage {
log.Println("Websocket closed") log.Println(COMPONENT + "Websocket closed")
return return
} }
buffer, err = darkneos.Transform(buffer, darkneos.ProtobufToRawBuf) buffer, err = darkneos.Transform(buffer, darkneos.ProtobufToRawBuf, &ctx)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
...@@ -124,17 +133,15 @@ func wsProxy(ws *websocket.Conn, Ch chan<- []byte, stopCh <-chan bool) { ...@@ -124,17 +133,15 @@ func wsProxy(ws *websocket.Conn, Ch chan<- []byte, stopCh <-chan bool) {
} }
} }
func tcpProxy(tcp net.Conn, Ch chan<- []byte, stopCh <-chan bool) { func tcpProxy(tcp net.Conn, Ch chan<- []byte, stopCh <-chan bool, ctx util.Context) {
const PACKET_LEN_PENDING = 2
defer tcp.Close() defer tcp.Close()
defer close(Ch) defer close(Ch)
reader := bufio.NewReader(tcp)
buffer := make([]byte, BUFFER_SIZE)
for { for {
select { select {
case _, ok := <-stopCh: case _, ok := <-stopCh:
log.Println("tcpProxy recv stop singal, exit. channel closed: ", ok) log.Println(COMPONENT+"tcpProxy recv stop singal, exit. channel closed: ", ok)
return return
default: default:
if err := tcp.SetReadDeadline(time.Now().Add(time.Second * TIME_OUT)); err != nil { if err := tcp.SetReadDeadline(time.Now().Add(time.Second * TIME_OUT)); err != nil {
...@@ -142,12 +149,8 @@ func tcpProxy(tcp net.Conn, Ch chan<- []byte, stopCh <-chan bool) { ...@@ -142,12 +149,8 @@ func tcpProxy(tcp net.Conn, Ch chan<- []byte, stopCh <-chan bool) {
return return
} }
_, err := reader.Read(buffer) guardBuf := make([]byte, PACKET_LEN_PENDING)
if err != nil { if _, err := tcp.Read(guardBuf); err != nil {
if err == io.EOF {
continue
}
if err, ok := err.(net.Error); ok && err.Timeout() { if err, ok := err.(net.Error); ok && err.Timeout() {
continue continue
} }
...@@ -156,13 +159,29 @@ func tcpProxy(tcp net.Conn, Ch chan<- []byte, stopCh <-chan bool) { ...@@ -156,13 +159,29 @@ func tcpProxy(tcp net.Conn, Ch chan<- []byte, stopCh <-chan bool) {
return return
} }
buffer, err = darkneos.Transform(buffer, darkneos.RawBufToProtobuf) packet_len := int(binary.LittleEndian.Uint16(guardBuf))
ctx.InfaReadBufferLen = PACKET_LEN_PENDING
buffer := make([]byte, packet_len+PACKET_LEN_PENDING)
copy(buffer, guardBuf)
if packet_len > 0 {
n, err := io.ReadAtLeast(tcp, buffer[PACKET_LEN_PENDING:], packet_len)
if err != nil && err != io.EOF {
log.Println(err)
return
}
ctx.InfaReadBufferLen += n
}
newBuffer, err := darkneos.Transform(buffer, darkneos.RawBufToProtobuf, &ctx)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
Ch <- buffer Ch <- newBuffer
} }
} }
} }
...@@ -178,5 +197,7 @@ func main() { ...@@ -178,5 +197,7 @@ func main() {
setupRoutes() setupRoutes()
log.Println(COMPONENT + "start listening on ws://localhost:" + TARGET_PORT)
log.Fatal(http.ListenAndServe(TARGET_PORT, nil)) log.Fatal(http.ListenAndServe(TARGET_PORT, nil))
} }
package util
type Context struct {
ContextId int64
WsConnected bool
TcpConnected bool
InfaReadBufferLen int
}
func NewContext() Context {
return Context{
ContextId: 0, // todo
WsConnected: false,
TcpConnected: false,
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment