|
|
|
@ -1,13 +1,13 @@ |
|
|
|
|
package main |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"bytes" |
|
|
|
|
"crypto/md5" |
|
|
|
|
"encoding/binary" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"strconv" |
|
|
|
|
"bytes" |
|
|
|
|
"crypto/md5" |
|
|
|
|
"encoding/binary" |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"strconv" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
const MT_BOOL_FALSE byte = 0x00 |
|
|
|
@ -25,415 +25,396 @@ const MT_REQUEST_ID = 0xFF0006 |
|
|
|
|
const MT_COMMAND = 0xFF0007 |
|
|
|
|
|
|
|
|
|
type M2Element struct { |
|
|
|
|
code int |
|
|
|
|
value interface{} |
|
|
|
|
code int |
|
|
|
|
value interface{} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (el *M2Element) String() string { |
|
|
|
|
return fmt.Sprintf("code=%v,type=%T,value=%v", el.code, el.value, el.value) |
|
|
|
|
return fmt.Sprintf("code=%v,type=%T,value=%v", el.code, el.value, el.value) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type M2Message struct { |
|
|
|
|
el []M2Element |
|
|
|
|
el []M2Element |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type M2Hash string |
|
|
|
|
|
|
|
|
|
func NewM2Message() *M2Message { |
|
|
|
|
m2 := M2Message{} |
|
|
|
|
return &m2 |
|
|
|
|
m2 := M2Message{} |
|
|
|
|
return &m2 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m2 *M2Message) Clear() { |
|
|
|
|
m2.el = []M2Element{} |
|
|
|
|
m2.el = []M2Element{} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m2 *M2Message) Append(code int, value interface{}) { |
|
|
|
|
if m2.el == nil { |
|
|
|
|
m2.Clear() |
|
|
|
|
} |
|
|
|
|
m2.el = append(m2.el, M2Element{code: code, value: value}) |
|
|
|
|
if m2.el == nil { |
|
|
|
|
m2.Clear() |
|
|
|
|
} |
|
|
|
|
m2.el = append(m2.el, M2Element{code: code, value: value}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m2 *M2Message) AppendElement(el *M2Element) { |
|
|
|
|
if m2.el == nil { |
|
|
|
|
m2.Clear() |
|
|
|
|
} |
|
|
|
|
m2.el = append(m2.el, *el) |
|
|
|
|
if m2.el == nil { |
|
|
|
|
m2.Clear() |
|
|
|
|
} |
|
|
|
|
m2.el = append(m2.el, *el) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m2 *M2Message) Bytes() []byte { |
|
|
|
|
res := []byte{} |
|
|
|
|
|
|
|
|
|
for _, el := range m2.el { |
|
|
|
|
buf := new(bytes.Buffer) |
|
|
|
|
|
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint16(el.code)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(el.code >> 16)) |
|
|
|
|
|
|
|
|
|
switch v := el.value.(type) { |
|
|
|
|
case bool: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, v) |
|
|
|
|
case byte: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_BYTE)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, v) |
|
|
|
|
case int: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_DWORD)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, int32(v)) |
|
|
|
|
case uint: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_DWORD)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint32(v)) |
|
|
|
|
case string: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_STRING)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(len(v))) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, []byte(v)) |
|
|
|
|
case M2Hash: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_HASH)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(len(v))) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, []byte(v)) |
|
|
|
|
case []byte: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_ARRAY)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint16(len(v))) |
|
|
|
|
for _, i := range v { |
|
|
|
|
binary.Write(buf, binary.LittleEndian, int32(i)) |
|
|
|
|
} |
|
|
|
|
case []int: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_ARRAY)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint16(len(v))) |
|
|
|
|
for _, i := range v { |
|
|
|
|
binary.Write(buf, binary.LittleEndian, int32(i)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res = append(res, buf.Bytes()...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
header := make([]byte, 6) |
|
|
|
|
header[0] = byte(len(res) + 4) |
|
|
|
|
header[1] = 0x01 |
|
|
|
|
header[2] = 0x00 |
|
|
|
|
header[3] = byte(len(res) + 2) |
|
|
|
|
header[4] = 0x4D |
|
|
|
|
header[5] = 0x32 |
|
|
|
|
|
|
|
|
|
return append(header, res...) |
|
|
|
|
res := []byte{} |
|
|
|
|
|
|
|
|
|
for _, el := range m2.el { |
|
|
|
|
buf := new(bytes.Buffer) |
|
|
|
|
|
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint16(el.code)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(el.code>>16)) |
|
|
|
|
|
|
|
|
|
switch v := el.value.(type) { |
|
|
|
|
case bool: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, v) |
|
|
|
|
case byte: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_BYTE)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, v) |
|
|
|
|
case int: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_DWORD)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, int32(v)) |
|
|
|
|
case uint: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_DWORD)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint32(v)) |
|
|
|
|
case string: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_STRING)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(len(v))) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, []byte(v)) |
|
|
|
|
case M2Hash: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_HASH)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(len(v))) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, []byte(v)) |
|
|
|
|
case []byte: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_ARRAY)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint16(len(v))) |
|
|
|
|
for _, i := range v { |
|
|
|
|
binary.Write(buf, binary.LittleEndian, int32(i)) |
|
|
|
|
} |
|
|
|
|
case []int: |
|
|
|
|
binary.Write(buf, binary.LittleEndian, byte(MT_ARRAY)) |
|
|
|
|
binary.Write(buf, binary.LittleEndian, uint16(len(v))) |
|
|
|
|
for _, i := range v { |
|
|
|
|
binary.Write(buf, binary.LittleEndian, int32(i)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res = append(res, buf.Bytes()...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
header := make([]byte, 6) |
|
|
|
|
header[0] = byte(len(res) + 4) |
|
|
|
|
header[1] = 0x01 |
|
|
|
|
header[2] = 0x00 |
|
|
|
|
header[3] = byte(len(res) + 2) |
|
|
|
|
header[4] = 0x4D |
|
|
|
|
header[5] = 0x32 |
|
|
|
|
|
|
|
|
|
return append(header, res...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (m2 *M2Message) ParseM2Element(buf io.Reader) error { |
|
|
|
|
var codeAndType uint32 |
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &codeAndType) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
el := M2Element{code: int(codeAndType & 0x00FFFFFF)} |
|
|
|
|
keyType := byte(codeAndType >> 24) |
|
|
|
|
log("lw", 3, "m2 code=%v type=%v", el.code, keyType) |
|
|
|
|
|
|
|
|
|
switch keyType { |
|
|
|
|
case MT_BOOL_FALSE, MT_BOOL_TRUE: |
|
|
|
|
el.value = keyType == MT_BOOL_TRUE |
|
|
|
|
log("lw", 3, "m2 MT_BOOL: %v", el.value.(bool)) |
|
|
|
|
case MT_BYTE: |
|
|
|
|
var b byte |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &b) |
|
|
|
|
el.value = b |
|
|
|
|
log("lw", 3, "m2 MT_BYTE: %v", el.value.(byte)) |
|
|
|
|
case MT_DWORD: |
|
|
|
|
var b int32 |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &b) |
|
|
|
|
el.value = b |
|
|
|
|
log("lw", 3, "m2 MT_DWORD: %v", el.value.(int32)) |
|
|
|
|
|
|
|
|
|
case MT_STRING: |
|
|
|
|
var length byte |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &length) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bs := make([]byte, length) |
|
|
|
|
_, err = io.ReadFull(buf, bs) |
|
|
|
|
el.value = string(bs) |
|
|
|
|
log("lw", 3, "m2 MT_STRING (len %v): %v", length, el.value.(string)) |
|
|
|
|
|
|
|
|
|
case MT_HASH: |
|
|
|
|
var length byte |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &length) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bs := make([]byte, length) |
|
|
|
|
_, err = io.ReadFull(buf, bs) |
|
|
|
|
el.value = M2Hash(bs) |
|
|
|
|
log("lw", 3, "m2 MT_HASH (len %v): %v", length, []byte(el.value.(M2Hash))) |
|
|
|
|
|
|
|
|
|
case MT_ARRAY: |
|
|
|
|
var length uint16 |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &length) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sl := []int{} |
|
|
|
|
for i := 0; i < int(length); i++ { |
|
|
|
|
var el2 int32 |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &el2) |
|
|
|
|
if err != nil { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
sl = append(sl, int(el2)) |
|
|
|
|
} |
|
|
|
|
el.value = sl |
|
|
|
|
log("lw", 3, "m2 MT_HASH (len %v): %v", length, el.value.([]int)) |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
return errors.New("unknown key code " + strconv.Itoa(int(keyType))) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
m2.el = append(m2.el, el) |
|
|
|
|
return nil |
|
|
|
|
var codeAndType uint32 |
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &codeAndType) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
el := M2Element{code: int(codeAndType & 0x00FFFFFF)} |
|
|
|
|
keyType := byte(codeAndType >> 24) |
|
|
|
|
log("lw", 3, "m2 code=%v type=%v", el.code, keyType) |
|
|
|
|
|
|
|
|
|
switch keyType { |
|
|
|
|
case MT_BOOL_FALSE, MT_BOOL_TRUE: |
|
|
|
|
el.value = keyType == MT_BOOL_TRUE |
|
|
|
|
log("lw", 3, "m2 MT_BOOL: %v", el.value.(bool)) |
|
|
|
|
case MT_BYTE: |
|
|
|
|
var b byte |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &b) |
|
|
|
|
el.value = b |
|
|
|
|
log("lw", 3, "m2 MT_BYTE: %v", el.value.(byte)) |
|
|
|
|
case MT_DWORD: |
|
|
|
|
var b int32 |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &b) |
|
|
|
|
el.value = b |
|
|
|
|
log("lw", 3, "m2 MT_DWORD: %v", el.value.(int32)) |
|
|
|
|
|
|
|
|
|
case MT_STRING: |
|
|
|
|
var length byte |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &length) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bs := make([]byte, length) |
|
|
|
|
_, err = io.ReadFull(buf, bs) |
|
|
|
|
el.value = string(bs) |
|
|
|
|
log("lw", 3, "m2 MT_STRING (len %v): %v", length, el.value.(string)) |
|
|
|
|
|
|
|
|
|
case MT_HASH: |
|
|
|
|
var length byte |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &length) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bs := make([]byte, length) |
|
|
|
|
_, err = io.ReadFull(buf, bs) |
|
|
|
|
el.value = M2Hash(bs) |
|
|
|
|
log("lw", 3, "m2 MT_HASH (len %v): %v", length, []byte(el.value.(M2Hash))) |
|
|
|
|
|
|
|
|
|
case MT_ARRAY: |
|
|
|
|
var length uint16 |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &length) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sl := []int{} |
|
|
|
|
for i := 0; i < int(length); i++ { |
|
|
|
|
var el2 int32 |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &el2) |
|
|
|
|
if err != nil { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
sl = append(sl, int(el2)) |
|
|
|
|
} |
|
|
|
|
el.value = sl |
|
|
|
|
log("lw", 3, "m2 MT_HASH (len %v): %v", length, el.value.([]int)) |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
return errors.New("unknown key code " + strconv.Itoa(int(keyType))) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
m2.el = append(m2.el, el) |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (m2 *M2Message) ParseM2Message(buf io.Reader) error { |
|
|
|
|
var headerBlockSize, m2BlockSize byte |
|
|
|
|
var m2Extra, m2Header uint16 |
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &headerBlockSize) |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &m2Extra) |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &m2BlockSize) |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &m2Header) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
if m2Extra != 0x1 { |
|
|
|
|
return errors.New("invalid M2_EXTRA") |
|
|
|
|
} |
|
|
|
|
if m2Header != 0x324D { |
|
|
|
|
return errors.New("invalid M2_HEADER") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for { |
|
|
|
|
log("lw", 3, "parsing new m2 element") |
|
|
|
|
err := m2.ParseM2Element(buf) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
var headerBlockSize, m2BlockSize byte |
|
|
|
|
var m2Extra, m2Header uint16 |
|
|
|
|
err := binary.Read(buf, binary.LittleEndian, &headerBlockSize) |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &m2Extra) |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &m2BlockSize) |
|
|
|
|
err = binary.Read(buf, binary.LittleEndian, &m2Header) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
if m2Extra != 0x1 { |
|
|
|
|
return errors.New("invalid M2_EXTRA") |
|
|
|
|
} |
|
|
|
|
if m2Header != 0x324D { |
|
|
|
|
return errors.New("invalid M2_HEADER") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for { |
|
|
|
|
log("lw", 3, "parsing new m2 element") |
|
|
|
|
err := m2.ParseM2Element(buf) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func ParseM2Messages(src []byte) (messages []M2Message, err error) { |
|
|
|
|
messages = []M2Message{} |
|
|
|
|
messages = []M2Message{} |
|
|
|
|
buf := bytes.NewReader(src) |
|
|
|
|
|
|
|
|
|
for { |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
err := m2.ParseM2Message(buf) |
|
|
|
|
|
|
|
|
|
if err == io.EOF { |
|
|
|
|
messages = append(messages, *m2) |
|
|
|
|
break |
|
|
|
|
} else if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} else { |
|
|
|
|
messages = append(messages, *m2) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log("lw", 3, "m2 eof after %v messages", len(messages)) |
|
|
|
|
return messages, nil |
|
|
|
|
for { |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
err := m2.ParseM2Message(buf) |
|
|
|
|
|
|
|
|
|
if err == io.EOF { |
|
|
|
|
messages = append(messages, *m2) |
|
|
|
|
break |
|
|
|
|
} else if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} else { |
|
|
|
|
messages = append(messages, *m2) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 3, "m2 eof after %v messages", len(messages)) |
|
|
|
|
return messages, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type LegacyWinbox struct { |
|
|
|
|
task *Task |
|
|
|
|
stage int |
|
|
|
|
m2 []M2Message |
|
|
|
|
task *Task |
|
|
|
|
conn *Connection |
|
|
|
|
stage int |
|
|
|
|
m2 []M2Message |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewLegacyWinbox(task *Task) *LegacyWinbox { |
|
|
|
|
lw := LegacyWinbox{task: task, stage: -1, m2: []M2Message{}} |
|
|
|
|
return &lw |
|
|
|
|
func NewLegacyWinbox(task *Task, conn *Connection) *LegacyWinbox { |
|
|
|
|
lw := LegacyWinbox{task: task, conn: conn, stage: -1, m2: []M2Message{}} |
|
|
|
|
return &lw |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// req1
|
|
|
|
|
func (lw *LegacyWinbox) MTReqList() []byte { |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
m2.Append(MT_RECEIVER, []byte{2, 2}) |
|
|
|
|
m2.Append(MT_COMMAND, byte(7)) |
|
|
|
|
m2.Append(MT_REQUEST_ID, byte(1)) |
|
|
|
|
m2.Append(MT_REPLY_EXPECTED, true) |
|
|
|
|
m2.Append(1, "list") |
|
|
|
|
return m2.Bytes() |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
m2.Append(MT_RECEIVER, []byte{2, 2}) |
|
|
|
|
m2.Append(MT_COMMAND, byte(7)) |
|
|
|
|
m2.Append(MT_REQUEST_ID, byte(1)) |
|
|
|
|
m2.Append(MT_REPLY_EXPECTED, true) |
|
|
|
|
m2.Append(1, "list") |
|
|
|
|
return m2.Bytes() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// res1
|
|
|
|
|
func (lw *LegacyWinbox) MTGetSid(m2 []M2Message) *M2Element { |
|
|
|
|
for _, msg := range m2 { |
|
|
|
|
for _, el := range msg.el { |
|
|
|
|
if el.code == 0xFE0001 { |
|
|
|
|
return &el |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
for _, msg := range m2 { |
|
|
|
|
for _, el := range msg.el { |
|
|
|
|
if el.code == 0xFE0001 { |
|
|
|
|
return &el |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// req2
|
|
|
|
|
func (lw *LegacyWinbox) MTReqChallenge(sid *M2Element) []byte { |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
m2.Append(MT_RECEIVER, []byte{13, 4}) |
|
|
|
|
m2.Append(MT_COMMAND, byte(4)) |
|
|
|
|
m2.Append(MT_REQUEST_ID, byte(2)) |
|
|
|
|
m2.AppendElement(sid) |
|
|
|
|
m2.Append(MT_REPLY_EXPECTED, true) |
|
|
|
|
return m2.Bytes() |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
m2.Append(MT_RECEIVER, []byte{13, 4}) |
|
|
|
|
m2.Append(MT_COMMAND, byte(4)) |
|
|
|
|
m2.Append(MT_REQUEST_ID, byte(2)) |
|
|
|
|
m2.AppendElement(sid) |
|
|
|
|
m2.Append(MT_REPLY_EXPECTED, true) |
|
|
|
|
return m2.Bytes() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// res2
|
|
|
|
|
func (lw *LegacyWinbox) MTGetSalt(m2 []M2Message) M2Hash { |
|
|
|
|
for _, msg := range m2 { |
|
|
|
|
for _, el := range msg.el { |
|
|
|
|
if el.code == 0x9 { |
|
|
|
|
return el.value.(M2Hash) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return "" |
|
|
|
|
for _, msg := range m2 { |
|
|
|
|
for _, el := range msg.el { |
|
|
|
|
if el.code == 0x9 { |
|
|
|
|
return el.value.(M2Hash) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return "" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// req3
|
|
|
|
|
func (lw *LegacyWinbox) MTReqAuth(sid *M2Element, login, digest, salt string) []byte { |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
m2.Append(MT_RECEIVER, []byte{13, 4}) |
|
|
|
|
m2.Append(MT_COMMAND, byte(1)) |
|
|
|
|
m2.Append(MT_REQUEST_ID, byte(3)) |
|
|
|
|
m2.AppendElement(sid) |
|
|
|
|
m2.Append(MT_REPLY_EXPECTED, true) |
|
|
|
|
m2.Append(1, login) |
|
|
|
|
m2.Append(9, M2Hash(salt)) |
|
|
|
|
m2.Append(10, M2Hash(digest)) |
|
|
|
|
return m2.Bytes() |
|
|
|
|
m2 := NewM2Message() |
|
|
|
|
m2.Append(MT_RECEIVER, []byte{13, 4}) |
|
|
|
|
m2.Append(MT_COMMAND, byte(1)) |
|
|
|
|
m2.Append(MT_REQUEST_ID, byte(3)) |
|
|
|
|
m2.AppendElement(sid) |
|
|
|
|
m2.Append(MT_REPLY_EXPECTED, true) |
|
|
|
|
m2.Append(1, login) |
|
|
|
|
m2.Append(9, M2Hash(salt)) |
|
|
|
|
m2.Append(10, M2Hash(digest)) |
|
|
|
|
return m2.Bytes() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// res3
|
|
|
|
|
func (lw *LegacyWinbox) MTGetResult(m2 []M2Message) (res bool, err error) { |
|
|
|
|
for _, msg := range m2 { |
|
|
|
|
for _, el := range msg.el { |
|
|
|
|
if el.code == 0xA { |
|
|
|
|
_, ok := el.value.(M2Hash) |
|
|
|
|
if ok { |
|
|
|
|
return true, nil |
|
|
|
|
} |
|
|
|
|
} else if el.code == 0xFF0008 { |
|
|
|
|
v, ok := el.value.(int32) |
|
|
|
|
if ok && v == 0xFE0006 { |
|
|
|
|
return false, nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false, errors.New("no auth marker found") |
|
|
|
|
for _, msg := range m2 { |
|
|
|
|
for _, el := range msg.el { |
|
|
|
|
if el.code == 0xA { |
|
|
|
|
_, ok := el.value.(M2Hash) |
|
|
|
|
if ok { |
|
|
|
|
return true, nil |
|
|
|
|
} |
|
|
|
|
} else if el.code == 0xFF0008 { |
|
|
|
|
v, ok := el.value.(int32) |
|
|
|
|
if ok && v == 0xFE0006 { |
|
|
|
|
return false, nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false, errors.New("no auth marker found") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (lw *LegacyWinbox) SendRecv(buf []byte) (res []byte, err error) { |
|
|
|
|
_, err = lw.task.conn.Write(buf) |
|
|
|
|
if err != nil { |
|
|
|
|
log("lw", 1, "failed to send: %v", err.Error()) |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
resp := make([]byte, 1024) |
|
|
|
|
n, err := lw.task.conn.Read(resp) |
|
|
|
|
if err != nil { |
|
|
|
|
log("lw", 1, "failed to recv: %v", err.Error()) |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return resp[:n], nil |
|
|
|
|
err = lw.conn.Send(buf) |
|
|
|
|
if err != nil { |
|
|
|
|
log("lw", 1, "failed to send: %v", err.Error()) |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
resp, err := lw.conn.Recv() |
|
|
|
|
if err != nil { |
|
|
|
|
log("lw", 1, "failed to recv: %v", err.Error()) |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return resp, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (lw *LegacyWinbox) TryLogin() (res bool, err error) { |
|
|
|
|
log("lw", 2, "login: stage 1, req_list") |
|
|
|
|
r1, err := lw.SendRecv(lw.MTReqList()) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 2, got response for req_list") |
|
|
|
|
msg, err := ParseM2Messages(r1) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sid := lw.MTGetSid(msg) |
|
|
|
|
if sid == nil { |
|
|
|
|
return false, errors.New("failed to get SID from stage 2") |
|
|
|
|
} |
|
|
|
|
log("lw", 2, "login: stage 2, sid %v", sid.String()) |
|
|
|
|
r2, err := lw.SendRecv(lw.MTReqChallenge(sid)) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 3, got response for req_challenge") |
|
|
|
|
log("lw", 2, "r2: %v", r2) |
|
|
|
|
|
|
|
|
|
msg, err = ParseM2Messages(r2) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
salt := lw.MTGetSalt(msg) |
|
|
|
|
if salt == "" { |
|
|
|
|
return false, errors.New("failed to get salt from stage 3") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sl := []byte{0} |
|
|
|
|
sl = append(sl, []byte(lw.task.password)...) |
|
|
|
|
sl = append(sl, []byte(salt)...) |
|
|
|
|
d := md5.Sum(sl) |
|
|
|
|
digest := append([]byte{0}, d[:]...) |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 3, hash %v", digest) |
|
|
|
|
r3, err := lw.SendRecv(lw.MTReqAuth(sid, lw.task.login, string(digest), string(salt))) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 4, got response for req_salt") |
|
|
|
|
msg, err = ParseM2Messages(r3) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res, err = lw.MTGetResult(msg) |
|
|
|
|
log("lw", 2, "login: stage 5: res=%v err=%v", res, err) |
|
|
|
|
|
|
|
|
|
return res, err |
|
|
|
|
log("lw", 2, "login: stage 1, req_list") |
|
|
|
|
r1, err := lw.SendRecv(lw.MTReqList()) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 2, got response for req_list") |
|
|
|
|
msg, err := ParseM2Messages(r1) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sid := lw.MTGetSid(msg) |
|
|
|
|
if sid == nil { |
|
|
|
|
return false, errors.New("failed to get SID from stage 2") |
|
|
|
|
} |
|
|
|
|
log("lw", 2, "login: stage 2, sid %v", sid.String()) |
|
|
|
|
r2, err := lw.SendRecv(lw.MTReqChallenge(sid)) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 3, got response for req_challenge") |
|
|
|
|
log("lw", 2, "r2: %v", r2) |
|
|
|
|
|
|
|
|
|
msg, err = ParseM2Messages(r2) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
salt := lw.MTGetSalt(msg) |
|
|
|
|
if salt == "" { |
|
|
|
|
return false, errors.New("failed to get salt from stage 3") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sl := []byte{0} |
|
|
|
|
sl = append(sl, []byte(lw.task.password)...) |
|
|
|
|
sl = append(sl, []byte(salt)...) |
|
|
|
|
d := md5.Sum(sl) |
|
|
|
|
digest := append([]byte{0}, d[:]...) |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 3, hash %v", digest) |
|
|
|
|
r3, err := lw.SendRecv(lw.MTReqAuth(sid, lw.task.login, string(digest), string(salt))) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
log("lw", 2, "login: stage 4, got response for req_salt") |
|
|
|
|
msg, err = ParseM2Messages(r3) |
|
|
|
|
if err != nil { |
|
|
|
|
return false, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res, err = lw.MTGetResult(msg) |
|
|
|
|
log("lw", 2, "login: stage 5: res=%v err=%v", res, err) |
|
|
|
|
|
|
|
|
|
return res, err |
|
|
|
|
} |
|
|
|
|