remove notion of services from oscar.Handler

This commit is contained in:
Artem Titoulenko 2021-12-17 19:01:22 -05:00
parent 0b40a2d6cf
commit 197e5a2f62
14 changed files with 105 additions and 72 deletions

View file

@ -6,10 +6,13 @@ import (
"crypto/md5" "crypto/md5"
"crypto/rand" "crypto/rand"
"encoding/base32" "encoding/base32"
"encoding/json"
"fmt"
"io" "io"
"aim-oscar/models" "aim-oscar/models"
"aim-oscar/oscar" "aim-oscar/oscar"
"aim-oscar/util"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/uptrace/bun" "github.com/uptrace/bun"
@ -19,7 +22,6 @@ 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 {
@ -31,12 +33,12 @@ func (a *AuthorizationRegistrationService) GenerateCipher() string {
return base32.StdEncoding.EncodeToString(randomBytes)[:CIPHER_LENGTH] 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 { switch snac.Header.Subtype {
// Request MD5 Auth Key // Request MD5 Auth Key
case 0x06: case 0x06:
tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes()) tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes())
panicIfError(err) util.PanicIfError(err)
usernameTLV := oscar.FindTLV(tlvs, 1) usernameTLV := oscar.FindTLV(tlvs, 1)
if usernameTLV == nil { if usernameTLV == nil {
@ -45,7 +47,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
// Fetch the user // Fetch the user
ctx := context.Background() 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 { if err != nil {
return err return err
} }
@ -60,7 +62,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
// Create cipher for this user // Create cipher for this user
user.Cipher = a.GenerateCipher() user.Cipher = a.GenerateCipher()
if err = user.Update(ctx, a.db); err != nil { if err = user.Update(ctx, db); err != nil {
return err return err
} }
@ -75,7 +77,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
// Client Authorization Request // Client Authorization Request
case 0x02: case 0x02:
tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes()) tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes())
panicIfError(err) util.PanicIfError(err)
usernameTLV := oscar.FindTLV(tlvs, 1) usernameTLV := oscar.FindTLV(tlvs, 1)
if usernameTLV == nil { if usernameTLV == nil {
@ -84,7 +86,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
username := string(usernameTLV.Data) username := string(usernameTLV.Data)
ctx := context.Background() ctx := context.Background()
user, err := models.UserByUsername(ctx, a.db, username) user, err := models.UserByUsername(ctx, db, username)
if err != nil { if err != nil {
return err return err
} }
@ -103,6 +105,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
return errors.New("missing password hash TLV 0x25") 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() h := md5.New()
io.WriteString(h, user.Cipher) io.WriteString(h, user.Cipher)
io.WriteString(h, user.Password) io.WriteString(h, user.Password)
@ -118,7 +121,7 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
badPasswordFlap.Data.WriteBinary(badPasswordSnac) badPasswordFlap.Data.WriteBinary(badPasswordSnac)
session.Send(badPasswordFlap) session.Send(badPasswordFlap)
// Tell tem to leave // Tell them to leave
discoFlap := oscar.NewFLAP(4) discoFlap := oscar.NewFLAP(4)
return session.Send(discoFlap) return session.Send(discoFlap)
} }
@ -126,13 +129,24 @@ func (a *AuthorizationRegistrationService) HandleSNAC(session *oscar.Session, sn
// Send BOS response + cookie // Send BOS response + cookie
authSnac := oscar.NewSNAC(0x17, 0x3) authSnac := oscar.NewSNAC(0x17, 0x3)
authSnac.Data.WriteBinary(usernameTLV) authSnac.Data.WriteBinary(usernameTLV)
authSnac.Data.WriteBinary(oscar.NewTLV(0x5, []byte("10.0.1.2:5191"))) authSnac.Data.WriteBinary(oscar.NewTLV(0x5, []byte(SRV_ADDRESS)))
authSnac.Data.WriteBinary(oscar.NewTLV(0x6, []byte(`{"hello": "uwu"}`)))
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 := oscar.NewFLAP(2)
authFlap.Data.WriteBinary(authSnac) authFlap.Data.WriteBinary(authSnac)
session.Send(authFlap) session.Send(authFlap)
// Tell tem to leave // Tell them to leave
discoFlap := oscar.NewFLAP(4) discoFlap := oscar.NewFLAP(4)
return session.Send(discoFlap) return session.Send(discoFlap)
} }

42
main.go
View file

@ -3,6 +3,8 @@ package main
import ( import (
"aim-oscar/models" "aim-oscar/models"
"aim-oscar/oscar" "aim-oscar/oscar"
"aim-oscar/util"
"bytes"
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
@ -26,6 +28,16 @@ const (
SRV_ADDRESS = SRV_HOST + ":" + SRV_PORT 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() { func main() {
// Set up the DB // Set up the DB
sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared") sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared")
@ -56,8 +68,34 @@ func main() {
} }
defer listener.Close() defer listener.Close()
handler := oscar.NewHandler() handler := oscar.NewHandler(func(session *oscar.Session, flap *oscar.FLAP) {
handler.RegisterService(0x17, &AuthorizationRegistrationService{db: db}) 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) 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)

View file

@ -11,6 +11,7 @@ import (
type User struct { type User struct {
bun.BaseModel `bun:"table:users"` bun.BaseModel `bun:"table:users"`
UIN int `bun:",pk,autoincrement"` UIN int `bun:",pk,autoincrement"`
Email string `bun:",unique"`
Username string `bun:",unique"` Username string `bun:",unique"`
Password string Password string
Cipher string Cipher string

View file

@ -2,3 +2,4 @@
rows: rows:
- username: toof - username: toof
password: bar password: bar
email: toof@plot.club

View file

@ -1,6 +1,7 @@
package oscar package oscar
import ( import (
"aim-oscar/util"
"encoding" "encoding"
"encoding/binary" "encoding/binary"
) )
@ -39,7 +40,7 @@ func (b *Buffer) Write(x []byte) (int, error) {
func (b *Buffer) WriteBinary(e encoding.BinaryMarshaler) { func (b *Buffer) WriteBinary(e encoding.BinaryMarshaler) {
d, err := e.MarshalBinary() d, err := e.MarshalBinary()
panicIfError(err) util.PanicIfError(err)
b.d = append(b.d, d...) b.d = append(b.d, d...)
} }

View file

@ -1,6 +1,7 @@
package oscar package oscar
import ( import (
"aim-oscar/util"
"bytes" "bytes"
"encoding" "encoding"
"encoding/binary" "encoding/binary"
@ -72,5 +73,5 @@ func (f *FLAP) Len() int {
} }
func (f *FLAP) String() string { 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()))
} }

View file

@ -1,6 +1,7 @@
package oscar package oscar
import ( import (
"aim-oscar/util"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -9,20 +10,16 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type Handler struct { type HandlerFunc func(*Session, *FLAP)
services map[uint16]Service
}
func NewHandler() *Handler { type Handler struct{ fn HandlerFunc }
func NewHandler(fn HandlerFunc) *Handler {
return &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) { func (h *Handler) Handle(conn net.Conn) {
session := NewSession(conn) session := NewSession(conn)
buf := make([]byte, 1024) buf := make([]byte, 1024)
@ -32,7 +29,7 @@ func (h *Handler) Handle(conn net.Conn) {
hello := NewFLAP(1) hello := NewFLAP(1)
hello.Data.Write([]byte{0, 0, 0, 1}) hello.Data.Write([]byte{0, 0, 0, 1})
err := session.Send(hello) err := session.Send(hello)
panicIfError(err) util.PanicIfError(err)
session.GreetedClient = true 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 // Try to parse all of the FLAPs in the buffer if we have enough bytes to
// fill a FLAP header // fill a FLAP header
for len(buf) >= 6 && buf[0] == 0x2a { for len(buf) >= 6 && buf[0] == 0x2a {
dataLength := Word(buf[4:6]) dataLength := util.Word(buf[4:6])
flapLength := int(dataLength) + 6 flapLength := int(dataLength) + 6
if len(buf) < flapLength { if len(buf) < flapLength {
log.Printf("not enough data, only %d bytes\n", len(buf)) log.Printf("not enough data, only %d bytes\n", len(buf))
@ -58,32 +55,11 @@ func (h *Handler) Handle(conn net.Conn) {
flap := &FLAP{} flap := &FLAP{}
if err := flap.UnmarshalBinary(buf[:flapLength]); err != nil { 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:] buf = buf[flapLength:]
fmt.Printf("%v ->\n%+v\n", conn.RemoteAddr(), flap) fmt.Printf("%v ->\n%+v\n", conn.RemoteAddr(), flap)
h.fn(session, 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)
}
}
} }
} }
} }

View file

@ -1,5 +0,0 @@
package oscar
type Service interface {
HandleSNAC(*Session, *SNAC) error
}

View file

@ -1,6 +1,7 @@
package oscar package oscar
import ( import (
"aim-oscar/util"
"context" "context"
"fmt" "fmt"
"net" "net"
@ -53,7 +54,7 @@ func (s *Session) Send(flap *FLAP) error {
return errors.Wrap(err, "could not marshal message") 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) _, err = s.Conn.Write(bytes)
return errors.Wrap(err, "could not write to client connection") return errors.Wrap(err, "could not write to client connection")
} }

View file

@ -1,6 +1,7 @@
package oscar package oscar
import ( import (
"aim-oscar/util"
"encoding" "encoding"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
@ -42,8 +43,8 @@ func (t *TLV) UnmarshalBinary(data []byte) error {
if len(data) < 4 { if len(data) < 4 {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
t.Type = Word(data[:2]) t.Type = util.Word(data[:2])
t.DataLength = Word(data[2:4]) t.DataLength = util.Word(data[2:4])
if len(data) < 4+int(t.DataLength) { if len(data) < 4+int(t.DataLength) {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
@ -53,7 +54,7 @@ func (t *TLV) UnmarshalBinary(data []byte) error {
} }
func (t *TLV) String() string { 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) { func UnmarshalTLVs(data []byte) ([]*TLV, error) {

11
service.go Normal file
View file

@ -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
}

View file

@ -45,7 +45,7 @@ const server = net.createServer((socket) => {
// new Administration(comm), // new Administration(comm),
// new Popups(comm), // new Popups(comm),
// new PrivacyManagement(comm), // new PrivacyManagement(comm),
new UserLookup(comm), // new UserLookup(comm),
// new UsageStats(comm), // new UsageStats(comm),
// new ChatNavigation(comm), // new ChatNavigation(comm),
// new Chat(comm), // new Chat(comm),

View file

@ -1,7 +0,0 @@
package main
func panicIfError(err error) {
if err != nil {
panic(err)
}
}

View file

@ -1,4 +1,4 @@
package oscar package util
import ( import (
"encoding/hex" "encoding/hex"
@ -9,7 +9,7 @@ import (
// splitBy splits string in chunks of n // splitBy splits string in chunks of n
// taken from: https://stackoverflow.com/a/69403603 // taken from: https://stackoverflow.com/a/69403603
func splitBy(s string, n int) []string { func SplitBy(s string, n int) []string {
var ss []string var ss []string
for i := 1; i < len(s); i++ { for i := 1; i < len(s); i++ {
if i%n == 0 { if i%n == 0 {
@ -22,13 +22,13 @@ func splitBy(s string, n int) []string {
return ss return ss
} }
func prettyBytes(bytes []byte) string { func PrettyBytes(bytes []byte) string {
hexStr := hex.EncodeToString(bytes) hexStr := hex.EncodeToString(bytes)
rows := splitBy(hexStr, 16) rows := SplitBy(hexStr, 16)
res := "" res := ""
for _, row := range rows { for _, row := range rows {
byteGroups := splitBy(row, 2) byteGroups := SplitBy(row, 2)
// Align string view to full 16 bytes + spaces // Align string view to full 16 bytes + spaces
res += fmt.Sprintf("%-23s", strings.Join(byteGroups, " ")) res += fmt.Sprintf("%-23s", strings.Join(byteGroups, " "))
@ -47,7 +47,7 @@ func prettyBytes(bytes []byte) string {
return strings.TrimSpace(res) return strings.TrimSpace(res)
} }
func panicIfError(err error) { func PanicIfError(err error) {
if err != nil { if err != nil {
panic(err) panic(err)
} }