80 lines
2.6 KiB
Go
80 lines
2.6 KiB
Go
package layerwire
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
|
|
"github.com/gotd/td/bin"
|
|
)
|
|
|
|
const maxOpaqueRequestBytes = 16 << 20
|
|
|
|
//go:embed schema/routable-compat.tl
|
|
var routableCompatSchema string
|
|
|
|
// routable combines the canonical Layer 227 model with the small set of
|
|
// explicitly declared compatibility-only methods. Nested objects in those
|
|
// methods are canonical Input* constructors, so one combined graph is needed
|
|
// for the same depth/vector/bytes walker to validate the complete request.
|
|
var routable = mustLoadRoutable()
|
|
|
|
func mustLoadRoutable() *schemaModel {
|
|
compat, err := parseSchemaModel(routableCompatSchema)
|
|
if err != nil {
|
|
panic("layerwire: parse routable compat schema: " + err.Error())
|
|
}
|
|
m := &schemaModel{
|
|
byCRC: make(map[uint32]*ctorLayout, len(canonical.byCRC)+len(compat.byCRC)),
|
|
byName: make(map[string]*ctorLayout, len(canonical.byName)+len(compat.byName)),
|
|
bareByT: make(map[string]*ctorLayout, len(canonical.bareByT)),
|
|
ctorsOfT: make(map[string][]*ctorLayout, len(canonical.ctorsOfT)),
|
|
}
|
|
for id, cl := range canonical.byCRC {
|
|
m.byCRC[id] = cl
|
|
}
|
|
for name, cl := range canonical.byName {
|
|
m.byName[name] = cl
|
|
}
|
|
for name, cl := range canonical.bareByT {
|
|
m.bareByT[name] = cl
|
|
}
|
|
for name, ctors := range canonical.ctorsOfT {
|
|
m.ctorsOfT[name] = ctors
|
|
}
|
|
for id, cl := range compat.byCRC {
|
|
if existing := m.byCRC[id]; existing != nil {
|
|
panic(fmt.Sprintf("layerwire: routable compat crc %#08x collides with %s", id, existing.name))
|
|
}
|
|
m.byCRC[id] = cl
|
|
m.byName[cl.name] = cl
|
|
}
|
|
return m
|
|
}
|
|
|
|
// ValidateRoutableRequest validates every request shape the router knows how to
|
|
// decode, including compatibility-only fallback methods. known=false denotes
|
|
// a genuinely unknown top-level constructor. Such a request is never decoded:
|
|
// it is treated as opaque, word-aligned TL data, bounded by both this total-size
|
|
// cap and mtprotoedge's transport/RPC budgets, and must continue to the router's
|
|
// compatibility trace rather than being mislabeled as malformed input.
|
|
func ValidateRoutableRequest(body []byte) (known bool, err error) {
|
|
b := &bin.Buffer{Buf: body}
|
|
id, err := b.PeekID()
|
|
if err != nil {
|
|
return false, classifyWalkError(err)
|
|
}
|
|
cl := routable.byCRC[id]
|
|
if cl == nil {
|
|
if len(body) > maxOpaqueRequestBytes {
|
|
return false, limitf("opaque request length %d exceeds limit %d", len(body), maxOpaqueRequestBytes)
|
|
}
|
|
if len(body)%bin.Word != 0 {
|
|
return false, malformedf("opaque request length %d is not word aligned", len(body))
|
|
}
|
|
return false, nil
|
|
}
|
|
if !cl.isFunc {
|
|
return true, malformedf("constructor %s (%#08x) is not a method", cl.name, id)
|
|
}
|
|
return true, validateRequestLayout(routable, cl, body)
|
|
}
|