From 197e5a2f62b93c3f18032c1afd5ebca7efb998d1 Mon Sep 17 00:00:00 2001 From: Artem Titoulenko Date: Fri, 17 Dec 2021 19:01:22 -0500 Subject: [PATCH] remove notion of services from oscar.Handler --- 0x17_authorization_registration_service.go | 36 ++++++++++++------ main.go | 42 ++++++++++++++++++++- models/User.go | 1 + models/fixtures.yml | 1 + oscar/buf.go | 3 +- oscar/flap.go | 3 +- oscar/server.go | 44 +++++----------------- oscar/service.go | 5 --- oscar/session.go | 3 +- oscar/tlv.go | 7 ++-- service.go | 11 ++++++ src/main-chat.ts | 2 +- util.go | 7 ---- {oscar => util}/util.go | 12 +++--- 14 files changed, 105 insertions(+), 72 deletions(-) delete mode 100644 oscar/service.go create mode 100644 service.go delete mode 100644 util.go rename {oscar => util}/util.go (83%) diff --git a/0x17_authorization_registration_service.go b/0x17_authorization_registration_service.go index 703677b..281e26d 100644 --- a/0x17_authorization_registration_service.go +++ b/0x17_authorization_registration_service.go @@ -6,10 +6,13 @@ import ( "crypto/md5" "crypto/rand" "encoding/base32" + "encoding/json" + "fmt" "io" "aim-oscar/models" "aim-oscar/oscar" + "aim-oscar/util" "github.com/pkg/errors" "github.com/uptrace/bun" @@ -19,7 +22,6 @@ const CIPHER_LENGTH = 64 const AIM_MD5_STRING = "AOL Instant Messenger (SM)" type AuthorizationRegistrationService struct { - db *bun.DB } func (a *AuthorizationRegistrationService) GenerateCipher() string { @@ -31,12 +33,12 @@ func (a *AuthorizationRegistrationService) GenerateCipher() string { return base32.StdEncoding.EncodeToString(randomBytes)[:CIPHER_LENGTH] } -func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, snac *oscar.SNAC) error { +func (a *AuthorizationRegistrationService) HandleSNAC(db *bun.DB, session *oscar.Session, snac *oscar.SNAC) error { switch snac.Header.Subtype { // Request MD5 Auth Key case 0x06: tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes()) - panicIfError(err) + util.PanicIfError(err) usernameTLV := oscar.FindTLV(tlvs, 1) if usernameTLV == nil { @@ -45,7 +47,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn // Fetch the user ctx := context.Background() - user, err := models.UserByUsername(ctx, a.db, string(usernameTLV.Data)) + user, err := models.UserByUsername(ctx, db, string(usernameTLV.Data)) if err != nil { return err } @@ -60,7 +62,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn // Create cipher for this user user.Cipher = a.GenerateCipher() - if err = user.Update(ctx, a.db); err != nil { + if err = user.Update(ctx, db); err != nil { return err } @@ -75,7 +77,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn // Client Authorization Request case 0x02: tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes()) - panicIfError(err) + util.PanicIfError(err) usernameTLV := oscar.FindTLV(tlvs, 1) if usernameTLV == nil { @@ -84,7 +86,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn username := string(usernameTLV.Data) ctx := context.Background() - user, err := models.UserByUsername(ctx, a.db, username) + user, err := models.UserByUsername(ctx, db, username) if err != nil { return err } @@ -103,6 +105,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn return errors.New("missing password hash TLV 0x25") } + // Compute password has that we expect the client to send back if the password was right h := md5.New() io.WriteString(h, user.Cipher) io.WriteString(h, user.Password) @@ -118,7 +121,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn badPasswordFlap.Data.WriteBinary(badPasswordSnac) session.Send(badPasswordFlap) - // Tell tem to leave + // Tell them to leave discoFlap := oscar.NewFLAP(4) return session.Send(discoFlap) } @@ -126,13 +129,24 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn // Send BOS response + cookie authSnac := oscar.NewSNAC(0x17, 0x3) authSnac.Data.WriteBinary(usernameTLV) - authSnac.Data.WriteBinary(oscar.NewTLV(0x5, []byte("10.0.1.2:5191"))) - authSnac.Data.WriteBinary(oscar.NewTLV(0x6, []byte(`{"hello": "uwu"}`))) + authSnac.Data.WriteBinary(oscar.NewTLV(0x5, []byte(SRV_ADDRESS))) + + cookie, err := json.Marshal(struct { + UIN int + X string + }{ + UIN: user.UIN, + X: fmt.Sprintf("%x", expectedPasswordHash), + }) + util.PanicIfError(err) + + authSnac.Data.WriteBinary(oscar.NewTLV(0x6, cookie)) + authSnac.Data.WriteBinary(oscar.NewTLV(0x11, []byte(user.Email))) authFlap := oscar.NewFLAP(2) authFlap.Data.WriteBinary(authSnac) session.Send(authFlap) - // Tell tem to leave + // Tell them to leave discoFlap := oscar.NewFLAP(4) return session.Send(discoFlap) } diff --git a/main.go b/main.go index ec5f1ad..8b96101 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "aim-oscar/models" "aim-oscar/oscar" + "aim-oscar/util" + "bytes" "context" "database/sql" "fmt" @@ -26,6 +28,16 @@ const ( SRV_ADDRESS = SRV_HOST + ":" + SRV_PORT ) +var services map[uint16]Service + +func init() { + services = make(map[uint16]Service) +} + +func RegisterService(family uint16, service Service) { + services[family] = service +} + func main() { // Set up the DB sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared") @@ -56,8 +68,34 @@ func main() { } defer listener.Close() - handler := oscar.NewHandler() - handler.RegisterService(0x17, &AuthorizationRegistrationService{db: db}) + handler := oscar.NewHandler(func(session *oscar.Session, flap *oscar.FLAP) { + if flap.Header.Channel == 1 { + // Is this a hello? + if bytes.Equal(flap.Data.Bytes(), []byte{0, 0, 0, 1}) { + return + } + } else if flap.Header.Channel == 2 { + snac := &oscar.SNAC{} + err := snac.UnmarshalBinary(flap.Data.Bytes()) + util.PanicIfError(err) + + fmt.Printf("%+v\n", snac) + if tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes()); err == nil { + for _, tlv := range tlvs { + fmt.Printf("%+v\n", tlv) + } + } else { + fmt.Printf("%s\n\n", util.PrettyBytes(snac.Data.Bytes())) + } + + if service, ok := services[snac.Header.Family]; ok { + err = service.HandleSNAC(db, session, snac) + util.PanicIfError(err) + } + } + }) + + RegisterService(0x17, &AuthorizationRegistrationService{}) exitChan := make(chan os.Signal, 1) signal.Notify(exitChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGABRT) diff --git a/models/User.go b/models/User.go index 2606ee4..842c6e5 100644 --- a/models/User.go +++ b/models/User.go @@ -11,6 +11,7 @@ import ( type User struct { bun.BaseModel `bun:"table:users"` UIN int `bun:",pk,autoincrement"` + Email string `bun:",unique"` Username string `bun:",unique"` Password string Cipher string diff --git a/models/fixtures.yml b/models/fixtures.yml index 125ce9c..bfed850 100644 --- a/models/fixtures.yml +++ b/models/fixtures.yml @@ -2,3 +2,4 @@ rows: - username: toof password: bar + email: toof@plot.club diff --git a/oscar/buf.go b/oscar/buf.go index 578f98e..c371651 100644 --- a/oscar/buf.go +++ b/oscar/buf.go @@ -1,6 +1,7 @@ package oscar import ( + "aim-oscar/util" "encoding" "encoding/binary" ) @@ -39,7 +40,7 @@ func (b *Buffer) Write(x []byte) (int, error) { func (b *Buffer) WriteBinary(e encoding.BinaryMarshaler) { d, err := e.MarshalBinary() - panicIfError(err) + util.PanicIfError(err) b.d = append(b.d, d...) } diff --git a/oscar/flap.go b/oscar/flap.go index 346b5b4..c1f5cec 100644 --- a/oscar/flap.go +++ b/oscar/flap.go @@ -1,6 +1,7 @@ package oscar import ( + "aim-oscar/util" "bytes" "encoding" "encoding/binary" @@ -72,5 +73,5 @@ func (f *FLAP) Len() int { } func (f *FLAP) String() string { - return fmt.Sprintf("FLAP(CH:%d, SEQ:%d):\n%s", f.Header.Channel, f.Header.SequenceNumber, prettyBytes(f.Data.Bytes())) + return fmt.Sprintf("FLAP(CH:%d, SEQ:%d):\n%s", f.Header.Channel, f.Header.SequenceNumber, util.PrettyBytes(f.Data.Bytes())) } diff --git a/oscar/server.go b/oscar/server.go index cc42fcf..1cb3786 100644 --- a/oscar/server.go +++ b/oscar/server.go @@ -1,6 +1,7 @@ package oscar import ( + "aim-oscar/util" "fmt" "io" "log" @@ -9,20 +10,16 @@ import ( "github.com/pkg/errors" ) -type Handler struct { - services map[uint16]Service -} +type HandlerFunc func(*Session, *FLAP) -func NewHandler() *Handler { +type Handler struct{ fn HandlerFunc } + +func NewHandler(fn HandlerFunc) *Handler { return &Handler{ - services: make(map[uint16]Service), + fn: fn, } } -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) @@ -32,7 +29,7 @@ func (h *Handler) Handle(conn net.Conn) { hello := NewFLAP(1) hello.Data.Write([]byte{0, 0, 0, 1}) err := session.Send(hello) - panicIfError(err) + util.PanicIfError(err) session.GreetedClient = true } @@ -49,7 +46,7 @@ func (h *Handler) Handle(conn net.Conn) { // 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]) + dataLength := util.Word(buf[4:6]) flapLength := int(dataLength) + 6 if len(buf) < flapLength { log.Printf("not enough data, only %d bytes\n", len(buf)) @@ -58,32 +55,11 @@ func (h *Handler) Handle(conn net.Conn) { flap := &FLAP{} if err := flap.UnmarshalBinary(buf[:flapLength]); err != nil { - panicIfError(errors.Wrap(err, "could not unmarshal FLAP")) + util.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) - } - } + h.fn(session, flap) } } } diff --git a/oscar/service.go b/oscar/service.go deleted file mode 100644 index 3c65c4b..0000000 --- a/oscar/service.go +++ /dev/null @@ -1,5 +0,0 @@ -package oscar - -type Service interface { - HandleSNAC(*Session, *SNAC) error -} diff --git a/oscar/session.go b/oscar/session.go index 692c898..093f648 100644 --- a/oscar/session.go +++ b/oscar/session.go @@ -1,6 +1,7 @@ package oscar import ( + "aim-oscar/util" "context" "fmt" "net" @@ -53,7 +54,7 @@ func (s *Session) Send(flap *FLAP) error { return errors.Wrap(err, "could not marshal message") } - fmt.Printf("-> %v\n%s\n\n", s.Conn.RemoteAddr(), prettyBytes(bytes)) + fmt.Printf("-> %v\n%s\n\n", s.Conn.RemoteAddr(), util.PrettyBytes(bytes)) _, err = s.Conn.Write(bytes) return errors.Wrap(err, "could not write to client connection") } diff --git a/oscar/tlv.go b/oscar/tlv.go index 0fc7fea..d1bfa4d 100644 --- a/oscar/tlv.go +++ b/oscar/tlv.go @@ -1,6 +1,7 @@ package oscar import ( + "aim-oscar/util" "encoding" "encoding/binary" "fmt" @@ -42,8 +43,8 @@ func (t *TLV) UnmarshalBinary(data []byte) error { if len(data) < 4 { return io.ErrUnexpectedEOF } - t.Type = Word(data[:2]) - t.DataLength = Word(data[2:4]) + t.Type = util.Word(data[:2]) + t.DataLength = util.Word(data[2:4]) if len(data) < 4+int(t.DataLength) { return io.ErrUnexpectedEOF } @@ -53,7 +54,7 @@ func (t *TLV) UnmarshalBinary(data []byte) error { } func (t *TLV) String() string { - return fmt.Sprintf("TLV(%#x):\n%s", t.Type, prettyBytes(t.Data)) + return fmt.Sprintf("TLV(%#x):\n%s", t.Type, util.PrettyBytes(t.Data)) } func UnmarshalTLVs(data []byte) ([]*TLV, error) { diff --git a/service.go b/service.go new file mode 100644 index 0000000..26b2850 --- /dev/null +++ b/service.go @@ -0,0 +1,11 @@ +package main + +import ( + "aim-oscar/oscar" + + "github.com/uptrace/bun" +) + +type Service interface { + HandleSNAC(*bun.DB, *oscar.Session, *oscar.SNAC) error +} diff --git a/src/main-chat.ts b/src/main-chat.ts index 86747bd..0cc7f0c 100644 --- a/src/main-chat.ts +++ b/src/main-chat.ts @@ -45,7 +45,7 @@ const server = net.createServer((socket) => { // new Administration(comm), // new Popups(comm), // new PrivacyManagement(comm), - new UserLookup(comm), + // new UserLookup(comm), // new UsageStats(comm), // new ChatNavigation(comm), // new Chat(comm), diff --git a/util.go b/util.go deleted file mode 100644 index d9a06cf..0000000 --- a/util.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -func panicIfError(err error) { - if err != nil { - panic(err) - } -} diff --git a/oscar/util.go b/util/util.go similarity index 83% rename from oscar/util.go rename to util/util.go index 8fe18e0..6c04d50 100644 --- a/oscar/util.go +++ b/util/util.go @@ -1,4 +1,4 @@ -package oscar +package util import ( "encoding/hex" @@ -9,7 +9,7 @@ import ( // splitBy splits string in chunks of n // taken from: https://stackoverflow.com/a/69403603 -func splitBy(s string, n int) []string { +func SplitBy(s string, n int) []string { var ss []string for i := 1; i < len(s); i++ { if i%n == 0 { @@ -22,13 +22,13 @@ func splitBy(s string, n int) []string { return ss } -func prettyBytes(bytes []byte) string { +func PrettyBytes(bytes []byte) string { hexStr := hex.EncodeToString(bytes) - rows := splitBy(hexStr, 16) + rows := SplitBy(hexStr, 16) res := "" for _, row := range rows { - byteGroups := splitBy(row, 2) + byteGroups := SplitBy(row, 2) // Align string view to full 16 bytes + spaces res += fmt.Sprintf("%-23s", strings.Join(byteGroups, " ")) @@ -47,7 +47,7 @@ func prettyBytes(bytes []byte) string { return strings.TrimSpace(res) } -func panicIfError(err error) { +func PanicIfError(err error) { if err != nil { panic(err) }