| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- package wire
- import (
- "bytes"
- "fmt"
- "io"
- )
- type SNACError struct {
- Code uint16
- TLVRestBlock
- }
- const (
- FLAPFrameSignon uint8 = 0x01
- FLAPFrameData uint8 = 0x02
- FLAPFrameError uint8 = 0x03
- FLAPFrameSignoff uint8 = 0x04
- FLAPFrameKeepAlive uint8 = 0x05
- )
- type FLAPFrame struct {
- StartMarker uint8
- FrameType uint8
- Sequence uint16
- PayloadLength uint16
- }
- func (f FLAPFrame) ReadBody(r io.Reader) (*bytes.Buffer, error) {
- b := make([]byte, f.PayloadLength)
- if f.PayloadLength > 0 {
- if _, err := io.ReadFull(r, b); err != nil {
- return nil, err
- }
- }
- return bytes.NewBuffer(b), nil
- }
- type SNACFrame struct {
- FoodGroup uint16
- SubGroup uint16
- Flags uint16
- RequestID uint32
- }
- type FLAPSignonFrame struct {
- FLAPVersion uint32
- TLVRestBlock
- }
- type SNACMessage struct {
- Frame SNACFrame
- Body any
- }
- // NewFlapClient creates a new FLAP client instance. startSeq is the initial
- // sequence value, which is typically 0. r receives FLAP messages, w writes
- // FLAP messages.
- func NewFlapClient(startSeq uint32, r io.Reader, w io.Writer) *FlapClient {
- return &FlapClient{
- sequence: startSeq,
- r: r,
- w: w,
- }
- }
- // FlapClient sends and receive FLAP frames to and from the server. It ensures
- // that the message sequence numbers are properly incremented after sending
- // each successive message. It is not safe to use with multiple goroutines
- // without synchronization.
- type FlapClient struct {
- sequence uint32
- r io.Reader
- w io.Writer
- }
- // SendSignonFrame sends a signon FLAP frame containing a list of TLVs to
- // authenticate or initiate a session.
- func (f *FlapClient) SendSignonFrame(tlvs []TLV) error {
- signonFrame := FLAPSignonFrame{
- FLAPVersion: 1,
- }
- if len(tlvs) > 0 {
- signonFrame.AppendList(tlvs)
- }
- buf := &bytes.Buffer{}
- if err := Marshal(signonFrame, buf); err != nil {
- return err
- }
- flap := FLAPFrame{
- StartMarker: 42,
- FrameType: FLAPFrameSignon,
- Sequence: uint16(f.sequence),
- PayloadLength: uint16(buf.Len()),
- }
- if err := Marshal(flap, f.w); err != nil {
- return err
- }
- if _, err := f.w.Write(buf.Bytes()); err != nil {
- return err
- }
- f.sequence++
- return nil
- }
- // ReceiveSignonFrame receives a signon FLAP response message.
- func (f *FlapClient) ReceiveSignonFrame() (FLAPSignonFrame, error) {
- flap := FLAPFrame{}
- if err := Unmarshal(&flap, f.r); err != nil {
- return FLAPSignonFrame{}, err
- }
- buf, err := flap.ReadBody(f.r)
- if err != nil {
- return FLAPSignonFrame{}, err
- }
- signonFrame := FLAPSignonFrame{}
- if err := Unmarshal(&signonFrame, buf); err != nil {
- return FLAPSignonFrame{}, err
- }
- return signonFrame, nil
- }
- // ReceiveFLAP receives a FLAP frame and body. It only returns a body if the
- // FLAP frame is a data frame.
- func (f *FlapClient) ReceiveFLAP() (FLAPFrame, *bytes.Buffer, error) {
- flap := FLAPFrame{}
- if err := Unmarshal(&flap, f.r); err != nil {
- return flap, nil, fmt.Errorf("unable to unmarshal FLAP frame: %w", err)
- }
- if flap.FrameType != FLAPFrameData {
- return flap, nil, nil
- }
- buf, err := flap.ReadBody(f.r)
- if err != nil {
- err = fmt.Errorf("unable to read FLAP body: %w", err)
- }
- return flap, buf, err
- }
- // SendSignoffFrame sends a sign-off FLAP frame with attached TLVs as the last
- // request sent in the FLAP auth flow. This is unrelated to the Disconnect()
- // method, which sends a sign-off frame to terminate a BOS connection.
- // todo: combine this method with Disconnect()
- func (f *FlapClient) SendSignoffFrame(tlvs TLVRestBlock) error {
- tlvBuf := &bytes.Buffer{}
- if err := Marshal(tlvs, tlvBuf); err != nil {
- return err
- }
- flap := FLAPFrame{
- StartMarker: 42,
- FrameType: FLAPFrameSignoff,
- Sequence: uint16(f.sequence),
- PayloadLength: uint16(tlvBuf.Len()),
- }
- if err := Marshal(flap, f.w); err != nil {
- return err
- }
- expectLen := tlvBuf.Len()
- c, err := f.w.Write(tlvBuf.Bytes())
- if err != nil {
- return err
- }
- if c != expectLen {
- panic("did not write the expected # of bytes")
- }
- f.sequence++
- return nil
- }
- // SendSNAC sends a SNAC message wrapped in a FLAP frame.
- func (f *FlapClient) SendSNAC(frame SNACFrame, body any) error {
- snacBuf := &bytes.Buffer{}
- if err := Marshal(frame, snacBuf); err != nil {
- return err
- }
- if err := Marshal(body, snacBuf); err != nil {
- return err
- }
- flap := FLAPFrame{
- StartMarker: 42,
- FrameType: FLAPFrameData,
- Sequence: uint16(f.sequence),
- PayloadLength: uint16(snacBuf.Len()),
- }
- if err := Marshal(flap, f.w); err != nil {
- return err
- }
- if _, err := f.w.Write(snacBuf.Bytes()); err != nil {
- return err
- }
- f.sequence++
- return nil
- }
- // ReceiveSNAC receives a SNAC message wrapped in a FLAP frame.
- func (f *FlapClient) ReceiveSNAC(frame *SNACFrame, body any) error {
- flap := FLAPFrame{}
- if err := Unmarshal(&flap, f.r); err != nil {
- return err
- }
- buf, err := flap.ReadBody(f.r)
- if err != nil {
- return err
- }
- if err := Unmarshal(frame, buf); err != nil {
- return err
- }
- return Unmarshal(body, buf)
- }
- // Disconnect sends a signoff FLAP frame.
- func (f *FlapClient) Disconnect() error {
- // gracefully disconnect so that the client does not try to
- // reconnect when the connection closes.
- flap := FLAPFrame{
- StartMarker: 42,
- FrameType: FLAPFrameSignoff,
- Sequence: uint16(f.sequence),
- PayloadLength: uint16(0),
- }
- return Marshal(flap, f.w)
- }
|