mirror of
https://github.com/amigan/aim-oscar-server.git
synced 2024-11-21 12:09:48 -05:00
move oscar server into it's own module
This commit is contained in:
parent
d4d7f71c66
commit
a5d86c47e5
13 changed files with 207 additions and 184 deletions
|
@ -8,16 +8,19 @@ import (
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"aim-oscar/models"
|
||||||
|
"aim-oscar/oscar"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
|
|
||||||
"aim-oscar/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const CIPHER_LENGTH = 64
|
const CIPHER_LENGTH = 64
|
||||||
const AIM_MD5_STRING = "AOL Instant Messenger (SM)"
|
const AIM_MD5_STRING = "AOL Instant Messenger (SM)"
|
||||||
|
|
||||||
type AuthorizationRegistrationService struct{}
|
type AuthorizationRegistrationService struct {
|
||||||
|
db *bun.DB
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AuthorizationRegistrationService) GenerateCipher() string {
|
func (a *AuthorizationRegistrationService) GenerateCipher() string {
|
||||||
randomBytes := make([]byte, 64)
|
randomBytes := make([]byte, 64)
|
||||||
|
@ -28,74 +31,74 @@ func (a *AuthorizationRegistrationService) GenerateCipher() string {
|
||||||
return base32.StdEncoding.EncodeToString(randomBytes)[:CIPHER_LENGTH]
|
return base32.StdEncoding.EncodeToString(randomBytes)[:CIPHER_LENGTH]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthorizationRegistrationService) HandleSNAC(db *bun.DB, session *Session, snac *SNAC) error {
|
func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, snac *oscar.SNAC) error {
|
||||||
switch snac.Header.Subtype {
|
switch snac.Header.Subtype {
|
||||||
// Request MD5 Auth Key
|
// Request MD5 Auth Key
|
||||||
case 0x06:
|
case 0x06:
|
||||||
tlvs, err := UnmarshalTLVs(snac.Data.Bytes())
|
tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes())
|
||||||
panicIfError(err)
|
panicIfError(err)
|
||||||
|
|
||||||
usernameTLV := FindTLV(tlvs, 1)
|
usernameTLV := oscar.FindTLV(tlvs, 1)
|
||||||
if usernameTLV == nil {
|
if usernameTLV == nil {
|
||||||
return errors.New("missing username TLV")
|
return errors.New("missing username TLV")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the user
|
// Fetch the user
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
user, err := models.UserByUsername(ctx, db, string(usernameTLV.Data))
|
user, err := models.UserByUsername(ctx, a.db, string(usernameTLV.Data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
snac := NewSNAC(0x17, 0x03)
|
snac := oscar.NewSNAC(0x17, 0x03)
|
||||||
snac.Data.WriteBinary(usernameTLV)
|
snac.Data.WriteBinary(usernameTLV)
|
||||||
snac.Data.WriteBinary(NewTLV(0x08, []byte{0, 4}))
|
snac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4}))
|
||||||
resp := NewFLAP(2)
|
resp := oscar.NewFLAP(2)
|
||||||
resp.Data.WriteBinary(snac)
|
resp.Data.WriteBinary(snac)
|
||||||
return session.Send(resp)
|
return session.Send(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create cipher for this user
|
// Create cipher for this user
|
||||||
user.Cipher = a.GenerateCipher()
|
user.Cipher = a.GenerateCipher()
|
||||||
if err = user.Update(ctx, db); err != nil {
|
if err = user.Update(ctx, a.db); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
snac := NewSNAC(0x17, 0x07)
|
snac := oscar.NewSNAC(0x17, 0x07)
|
||||||
snac.Data.WriteUint16(uint16(len(user.Cipher)))
|
snac.Data.WriteUint16(uint16(len(user.Cipher)))
|
||||||
snac.Data.WriteString(user.Cipher)
|
snac.Data.WriteString(user.Cipher)
|
||||||
|
|
||||||
resp := NewFLAP(2)
|
resp := oscar.NewFLAP(2)
|
||||||
resp.Data.WriteBinary(snac)
|
resp.Data.WriteBinary(snac)
|
||||||
return session.Send(resp)
|
return session.Send(resp)
|
||||||
|
|
||||||
// Client Authorization Request
|
// Client Authorization Request
|
||||||
case 0x02:
|
case 0x02:
|
||||||
tlvs, err := UnmarshalTLVs(snac.Data.Bytes())
|
tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes())
|
||||||
panicIfError(err)
|
panicIfError(err)
|
||||||
|
|
||||||
usernameTLV := FindTLV(tlvs, 1)
|
usernameTLV := oscar.FindTLV(tlvs, 1)
|
||||||
if usernameTLV == nil {
|
if usernameTLV == nil {
|
||||||
return errors.New("missing username TLV 0x1")
|
return errors.New("missing username TLV 0x1")
|
||||||
}
|
}
|
||||||
|
|
||||||
username := string(usernameTLV.Data)
|
username := string(usernameTLV.Data)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
user, err := models.UserByUsername(ctx, db, username)
|
user, err := models.UserByUsername(ctx, a.db, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
snac := NewSNAC(0x17, 0x03)
|
snac := oscar.NewSNAC(0x17, 0x03)
|
||||||
snac.Data.WriteBinary(usernameTLV)
|
snac.Data.WriteBinary(usernameTLV)
|
||||||
snac.Data.WriteBinary(NewTLV(0x08, []byte{0, 4}))
|
snac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4}))
|
||||||
resp := NewFLAP(2)
|
resp := oscar.NewFLAP(2)
|
||||||
resp.Data.WriteBinary(snac)
|
resp.Data.WriteBinary(snac)
|
||||||
return session.Send(resp)
|
return session.Send(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordHashTLV := FindTLV(tlvs, 0x25)
|
passwordHashTLV := oscar.FindTLV(tlvs, 0x25)
|
||||||
if passwordHashTLV == nil {
|
if passwordHashTLV == nil {
|
||||||
return errors.New("missing password hash TLV 0x25")
|
return errors.New("missing password hash TLV 0x25")
|
||||||
}
|
}
|
||||||
|
@ -108,29 +111,29 @@ func (a *AuthorizationRegistrationService) HandleSNAC(db *bun.DB, session *Sessi
|
||||||
|
|
||||||
if !bytes.Equal(expectedPasswordHash, passwordHashTLV.Data) {
|
if !bytes.Equal(expectedPasswordHash, passwordHashTLV.Data) {
|
||||||
// Tell the client this was a bad password
|
// Tell the client this was a bad password
|
||||||
badPasswordSnac := NewSNAC(0x17, 0x03)
|
badPasswordSnac := oscar.NewSNAC(0x17, 0x03)
|
||||||
badPasswordSnac.Data.WriteBinary(usernameTLV)
|
badPasswordSnac.Data.WriteBinary(usernameTLV)
|
||||||
badPasswordSnac.Data.WriteBinary(NewTLV(0x08, []byte{0, 4}))
|
badPasswordSnac.Data.WriteBinary(oscar.NewTLV(0x08, []byte{0, 4}))
|
||||||
badPasswordFlap := NewFLAP(2)
|
badPasswordFlap := oscar.NewFLAP(2)
|
||||||
badPasswordFlap.Data.WriteBinary(badPasswordSnac)
|
badPasswordFlap.Data.WriteBinary(badPasswordSnac)
|
||||||
session.Send(badPasswordFlap)
|
session.Send(badPasswordFlap)
|
||||||
|
|
||||||
// Tell tem to leave
|
// Tell tem to leave
|
||||||
discoFlap := NewFLAP(4)
|
discoFlap := oscar.NewFLAP(4)
|
||||||
return session.Send(discoFlap)
|
return session.Send(discoFlap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send BOS response + cookie
|
// Send BOS response + cookie
|
||||||
authSnac := NewSNAC(0x17, 0x3)
|
authSnac := oscar.NewSNAC(0x17, 0x3)
|
||||||
authSnac.Data.WriteBinary(usernameTLV)
|
authSnac.Data.WriteBinary(usernameTLV)
|
||||||
authSnac.Data.WriteBinary(NewTLV(0x5, []byte("10.0.1.2:5191")))
|
authSnac.Data.WriteBinary(oscar.NewTLV(0x5, []byte("10.0.1.2:5191")))
|
||||||
authSnac.Data.WriteBinary(NewTLV(0x6, []byte(`{"hello": "uwu"}`)))
|
authSnac.Data.WriteBinary(oscar.NewTLV(0x6, []byte(`{"hello": "uwu"}`)))
|
||||||
authFlap := NewFLAP(2)
|
authFlap := oscar.NewFLAP(2)
|
||||||
authFlap.Data.WriteBinary(authSnac)
|
authFlap.Data.WriteBinary(authSnac)
|
||||||
session.Send(authFlap)
|
session.Send(authFlap)
|
||||||
|
|
||||||
// Tell tem to leave
|
// Tell tem to leave
|
||||||
discoFlap := NewFLAP(4)
|
discoFlap := oscar.NewFLAP(4)
|
||||||
return session.Send(discoFlap)
|
return session.Send(discoFlap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
96
main.go
96
main.go
|
@ -2,17 +2,17 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"aim-oscar/models"
|
"aim-oscar/models"
|
||||||
|
"aim-oscar/oscar"
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bun/dbfixture"
|
"github.com/uptrace/bun/dbfixture"
|
||||||
"github.com/uptrace/bun/dialect/sqlitedialect"
|
"github.com/uptrace/bun/dialect/sqlitedialect"
|
||||||
|
@ -26,11 +26,7 @@ const (
|
||||||
SRV_ADDRESS = SRV_HOST + ":" + SRV_PORT
|
SRV_ADDRESS = SRV_HOST + ":" + SRV_PORT
|
||||||
)
|
)
|
||||||
|
|
||||||
var services = make(map[uint16]Service)
|
var db *bun.DB
|
||||||
|
|
||||||
func init() {
|
|
||||||
services[0x17] = &AuthorizationRegistrationService{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Set up the DB
|
// Set up the DB
|
||||||
|
@ -39,12 +35,16 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
db := bun.NewDB(sqldb, sqlitedialect.New())
|
db := bun.NewDB(sqldb, sqlitedialect.New())
|
||||||
|
db.SetConnMaxIdleTime(15 * time.Second)
|
||||||
|
db.SetConnMaxLifetime(1 * time.Minute)
|
||||||
|
|
||||||
// Print all queries to stdout.
|
// Print all queries to stdout.
|
||||||
db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
|
db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
|
||||||
|
|
||||||
|
// Register our DB models
|
||||||
db.RegisterModel((*models.User)(nil))
|
db.RegisterModel((*models.User)(nil))
|
||||||
|
|
||||||
|
// dev: load in fixtures to test against
|
||||||
fixture := dbfixture.New(db, dbfixture.WithRecreateTables())
|
fixture := dbfixture.New(db, dbfixture.WithRecreateTables())
|
||||||
err = fixture.Load(context.Background(), os.DirFS("models"), "fixtures.yml")
|
err = fixture.Load(context.Background(), os.DirFS("models"), "fixtures.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,6 +58,9 @@ func main() {
|
||||||
}
|
}
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
|
|
||||||
|
handler := oscar.NewHandler()
|
||||||
|
handler.RegisterService(0x17, &AuthorizationRegistrationService{db: db})
|
||||||
|
|
||||||
exitChan := make(chan os.Signal, 1)
|
exitChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(exitChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGABRT)
|
signal.Notify(exitChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGABRT)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -74,84 +77,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
session := NewSession(conn)
|
|
||||||
log.Printf("Connection from %v", conn.RemoteAddr())
|
log.Printf("Connection from %v", conn.RemoteAddr())
|
||||||
|
go handler.Handle(conn)
|
||||||
go handleTCPConnection(db, session, conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleTCPConnection(db *bun.DB, session *Session, conn net.Conn) {
|
|
||||||
// defer (func() {
|
|
||||||
// if err := recover(); err != nil {
|
|
||||||
// log.Printf("Error handling message: %+v\n", err.(error))
|
|
||||||
// }
|
|
||||||
// conn.Close()
|
|
||||||
// log.Printf("Closed connection to %v", conn.RemoteAddr())
|
|
||||||
// })()
|
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
for {
|
|
||||||
if !session.GreetedClient {
|
|
||||||
// send a hello
|
|
||||||
hello := NewFLAP(1)
|
|
||||||
hello.Data.Write([]byte{0, 0, 0, 1})
|
|
||||||
err := session.Send(hello)
|
|
||||||
panicIfError(err)
|
|
||||||
session.GreetedClient = true
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Println("Read Error: ", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse all of the FLAPs in the buffer if we have enough bytes to
|
|
||||||
// fill a FLAP header
|
|
||||||
for len(buf) >= 6 && buf[0] == 0x2a {
|
|
||||||
dataLength := Word(buf[4:6])
|
|
||||||
flapLength := int(dataLength) + 6
|
|
||||||
if len(buf) < flapLength {
|
|
||||||
log.Printf("not enough data, only %d bytes\n", len(buf))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
flap := &FLAP{}
|
|
||||||
if err := flap.UnmarshalBinary(buf[:flapLength]); err != nil {
|
|
||||||
panicIfError(errors.Wrap(err, "could not unmarshal FLAP"))
|
|
||||||
}
|
|
||||||
buf = buf[flapLength:]
|
|
||||||
fmt.Printf("%v ->\n%+v\n", conn.RemoteAddr(), flap)
|
|
||||||
handleMessage(db, session, flap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMessage(db *bun.DB, session *Session, flap *FLAP) {
|
|
||||||
if flap.Header.Channel == 1 {
|
|
||||||
|
|
||||||
} else if flap.Header.Channel == 2 {
|
|
||||||
snac := &SNAC{}
|
|
||||||
err := snac.UnmarshalBinary(flap.Data.Bytes())
|
|
||||||
panicIfError(err)
|
|
||||||
|
|
||||||
fmt.Printf("%+v\n", snac)
|
|
||||||
if tlvs, err := UnmarshalTLVs(snac.Data.Bytes()); err == nil {
|
|
||||||
for _, tlv := range tlvs {
|
|
||||||
fmt.Printf("%+v\n", tlv)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%s\n\n", prettyBytes(snac.Data.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if service, ok := services[snac.Header.Family]; ok {
|
|
||||||
err = service.HandleSNAC(db, session, snac)
|
|
||||||
panicIfError(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package oscar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding"
|
"encoding"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package oscar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package oscar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
89
oscar/server.go
Normal file
89
oscar/server.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package oscar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
services map[uint16]Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler() *Handler {
|
||||||
|
return &Handler{
|
||||||
|
services: make(map[uint16]Service),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) RegisterService(family uint16, service Service) {
|
||||||
|
h.services[family] = service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) Handle(conn net.Conn) {
|
||||||
|
session := NewSession(conn)
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
for {
|
||||||
|
if !session.GreetedClient {
|
||||||
|
// send a hello
|
||||||
|
hello := NewFLAP(1)
|
||||||
|
hello.Data.Write([]byte{0, 0, 0, 1})
|
||||||
|
err := session.Send(hello)
|
||||||
|
panicIfError(err)
|
||||||
|
session.GreetedClient = true
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
log.Println("Read Error: ", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse all of the FLAPs in the buffer if we have enough bytes to
|
||||||
|
// fill a FLAP header
|
||||||
|
for len(buf) >= 6 && buf[0] == 0x2a {
|
||||||
|
dataLength := Word(buf[4:6])
|
||||||
|
flapLength := int(dataLength) + 6
|
||||||
|
if len(buf) < flapLength {
|
||||||
|
log.Printf("not enough data, only %d bytes\n", len(buf))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
flap := &FLAP{}
|
||||||
|
if err := flap.UnmarshalBinary(buf[:flapLength]); err != nil {
|
||||||
|
panicIfError(errors.Wrap(err, "could not unmarshal FLAP"))
|
||||||
|
}
|
||||||
|
buf = buf[flapLength:]
|
||||||
|
fmt.Printf("%v ->\n%+v\n", conn.RemoteAddr(), flap)
|
||||||
|
|
||||||
|
if flap.Header.Channel == 1 {
|
||||||
|
|
||||||
|
} else if flap.Header.Channel == 2 {
|
||||||
|
snac := &SNAC{}
|
||||||
|
err := snac.UnmarshalBinary(flap.Data.Bytes())
|
||||||
|
panicIfError(err)
|
||||||
|
|
||||||
|
fmt.Printf("%+v\n", snac)
|
||||||
|
if tlvs, err := UnmarshalTLVs(snac.Data.Bytes()); err == nil {
|
||||||
|
for _, tlv := range tlvs {
|
||||||
|
fmt.Printf("%+v\n", tlv)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n\n", prettyBytes(snac.Data.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if service, ok := h.services[snac.Header.Family]; ok {
|
||||||
|
err = service.HandleSNAC(session, snac)
|
||||||
|
panicIfError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
oscar/service.go
Normal file
5
oscar/service.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package oscar
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
HandleSNAC(*Session, *SNAC) error
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package oscar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package oscar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package oscar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding"
|
"encoding"
|
64
oscar/util.go
Normal file
64
oscar/util.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package oscar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// splitBy splits string in chunks of n
|
||||||
|
// taken from: https://stackoverflow.com/a/69403603
|
||||||
|
func splitBy(s string, n int) []string {
|
||||||
|
var ss []string
|
||||||
|
for i := 1; i < len(s); i++ {
|
||||||
|
if i%n == 0 {
|
||||||
|
ss = append(ss, s[:i])
|
||||||
|
s = s[i:]
|
||||||
|
i = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss = append(ss, s)
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettyBytes(bytes []byte) string {
|
||||||
|
hexStr := hex.EncodeToString(bytes)
|
||||||
|
rows := splitBy(hexStr, 16)
|
||||||
|
|
||||||
|
res := ""
|
||||||
|
for _, row := range rows {
|
||||||
|
byteGroups := splitBy(row, 2)
|
||||||
|
// Align string view to full 16 bytes + spaces
|
||||||
|
res += fmt.Sprintf("%-23s", strings.Join(byteGroups, " "))
|
||||||
|
|
||||||
|
res += " |"
|
||||||
|
for _, r := range byteGroups {
|
||||||
|
n, err := strconv.ParseInt(r, 16, 8)
|
||||||
|
if err != nil || (n < 32 || n > 126) {
|
||||||
|
res += "."
|
||||||
|
} else {
|
||||||
|
res += string(rune(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res += "|\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func panicIfError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Word(b []byte) uint16 {
|
||||||
|
var _ = b[1]
|
||||||
|
return uint16(b[1]) | uint16(b[0])<<8
|
||||||
|
}
|
||||||
|
|
||||||
|
func DWord(b []byte) uint32 {
|
||||||
|
var _ = b[3]
|
||||||
|
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import "github.com/uptrace/bun"
|
|
||||||
|
|
||||||
type Service interface {
|
|
||||||
HandleSNAC(*bun.DB, *Session, *SNAC) error
|
|
||||||
}
|
|
57
util.go
57
util.go
|
@ -1,64 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// splitBy splits string in chunks of n
|
|
||||||
// taken from: https://stackoverflow.com/a/69403603
|
|
||||||
func splitBy(s string, n int) []string {
|
|
||||||
var ss []string
|
|
||||||
for i := 1; i < len(s); i++ {
|
|
||||||
if i%n == 0 {
|
|
||||||
ss = append(ss, s[:i])
|
|
||||||
s = s[i:]
|
|
||||||
i = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss = append(ss, s)
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
func prettyBytes(bytes []byte) string {
|
|
||||||
hexStr := hex.EncodeToString(bytes)
|
|
||||||
rows := splitBy(hexStr, 16)
|
|
||||||
|
|
||||||
res := ""
|
|
||||||
for _, row := range rows {
|
|
||||||
byteGroups := splitBy(row, 2)
|
|
||||||
// Align string view to full 16 bytes + spaces
|
|
||||||
res += fmt.Sprintf("%-23s", strings.Join(byteGroups, " "))
|
|
||||||
|
|
||||||
res += " |"
|
|
||||||
for _, r := range byteGroups {
|
|
||||||
n, err := strconv.ParseInt(r, 16, 8)
|
|
||||||
if err != nil || (n < 32 || n > 126) {
|
|
||||||
res += "."
|
|
||||||
} else {
|
|
||||||
res += string(rune(n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res += "|\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func panicIfError(err error) {
|
func panicIfError(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Word(b []byte) uint16 {
|
|
||||||
var _ = b[1]
|
|
||||||
return uint16(b[1]) | uint16(b[0])<<8
|
|
||||||
}
|
|
||||||
|
|
||||||
func DWord(b []byte) uint32 {
|
|
||||||
var _ = b[3]
|
|
||||||
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue