// handle Read Blob request. [Vol 3, Part F, 3.4.4.5 & 3.4.4.6] func (s *Server) handleReadBlobRequest(r ReadBlobRequest) []byte { // Validate the request. switch { case len(r) != 5: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) } a, ok := s.db.at(r.AttributeHandle()) if !ok { return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle) } rsp := ReadBlobResponse(s.txBuf) rsp.SetAttributeOpcode() buf := bytes.NewBuffer(rsp.PartAttributeValue()) buf.Reset() // Simple case. Read-only, no-authorization, no-authentication. if a.v != nil { binary.Write(buf, binary.LittleEndian, a.v) return rsp[:1+buf.Len()] } // Pass the request to upper layer with the ResponseWriter, which caps // the buffer to a valid length of payload. if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess { return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e) } return rsp[:1+buf.Len()] }
// NewServer returns an ATT (Attribute Protocol) server. func NewServer(db *DB, l2c ble.Conn) (*Server, error) { mtu := l2c.RxMTU() if mtu < ble.DefaultMTU || mtu > ble.MaxMTU { return nil, fmt.Errorf("invalid MTU") } // Although the rxBuf is initialized with the capacity of rxMTU, it is // not discovered, and only the default ATT_MTU (23 bytes) of it shall // be used until remote central request ExchangeMTU. s := &Server{ conn: &conn{ Conn: l2c, cccs: make(map[uint16]uint16), in: make(map[uint16]ble.Notifier), nn: make(map[uint16]ble.Notifier), }, db: db, rxMTU: mtu, txBuf: make([]byte, ble.DefaultMTU, ble.DefaultMTU), chNotBuf: make(chan []byte, 1), chIndBuf: make(chan []byte, 1), chConfirm: make(chan bool), dummyRspWriter: ble.NewResponseWriter(nil), } s.conn.svr = s s.chNotBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU) s.chIndBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU) return s, nil }
// handle Read By Type request. [Vol 3, Part F, 3.4.4.1 & 3.4.4.2] func (s *Server) handleReadByTypeRequest(r ReadByTypeRequest) []byte { // Validate the request. switch { case len(r) != 7 && len(r) != 21: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } rsp := ReadByTypeResponse(s.txBuf) rsp.SetAttributeOpcode() buf := bytes.NewBuffer(rsp.AttributeDataList()) buf.Reset() // handle length (2 bytes) + value length. // Each response shall only contains values with the same size. dlen := 0 for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { if !a.typ.Equal(ble.UUID(r.AttributeType())) { continue } v := a.v if v == nil { buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-2)) if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess { // Return if the first value read cause an error. if dlen == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e) } // Otherwise, skip to the next one. break } v = buf2.Bytes() } if dlen == 0 { // Found the first value. dlen = 2 + len(v) if dlen > 255 { dlen = 255 } if dlen > buf.Cap() { dlen = buf.Cap() } rsp.SetLength(uint8(dlen)) } else if 2+len(v) != dlen { break } if buf.Len()+dlen > buf.Cap() { break } binary.Write(buf, binary.LittleEndian, a.h) binary.Write(buf, binary.LittleEndian, v[:dlen-2]) } if dlen == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) } return rsp[:2+buf.Len()] }
// handle Read Blob request. [Vol 3, Part F, 3.4.4.9 & 3.4.4.10] func (s *Server) handleReadByGroupRequest(r ReadByGroupTypeRequest) []byte { // Validate the request. switch { case len(r) != 7 && len(r) != 21: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } rsp := ReadByGroupTypeResponse(s.txBuf) rsp.SetAttributeOpcode() buf := bytes.NewBuffer(rsp.AttributeDataList()) buf.Reset() dlen := 0 for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { v := a.v if v == nil { buf2 := bytes.NewBuffer(make([]byte, buf.Cap()-buf.Len()-4)) if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e) } v = buf2.Bytes() } if dlen == 0 { dlen = 4 + len(v) if dlen > 255 { dlen = 255 } if dlen > buf.Cap() { dlen = buf.Cap() } rsp.SetLength(uint8(dlen)) } else if 4+len(v) != dlen { break } if buf.Len()+dlen > buf.Cap() { break } binary.Write(buf, binary.LittleEndian, a.h) binary.Write(buf, binary.LittleEndian, a.endh) binary.Write(buf, binary.LittleEndian, v[:dlen-4]) } if dlen == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) } return rsp[:2+buf.Len()] }
// handle Find By Type Value request. [Vol 3, Part F, 3.4.3.3 & 3.4.3.4] func (s *Server) handleFindByTypeValueRequest(r FindByTypeValueRequest) []byte { // Validate the request. switch { case len(r) < 7: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } rsp := FindByTypeValueResponse(s.txBuf) rsp.SetAttributeOpcode() buf := bytes.NewBuffer(rsp.HandleInformationList()) buf.Reset() for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { v, starth, endh := a.v, a.h, a.endh if v == nil { // The value shall not exceed ATT_MTU - 7 bytes. // Since ResponseWriter caps the value at the capacity, // we allocate one extra byte, and the written length. buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-7+1)) e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)) if e != ble.ErrSuccess || buf2.Len() > len(s.txBuf)-7 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) } endh = a.h } if !(ble.UUID(v).Equal(ble.UUID(r.AttributeValue()))) { continue } if buf.Len()+4 > buf.Cap() { break } binary.Write(buf, binary.LittleEndian, starth) binary.Write(buf, binary.LittleEndian, endh) } if buf.Len() == 0 { return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) } return rsp[:1+buf.Len()] }
// handle Write request. [Vol 3, Part F, 3.4.5.1 & 3.4.5.2] func (s *Server) handleWriteRequest(r WriteRequest) []byte { // Validate the request. switch { case len(r) < 3: return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) } a, ok := s.db.at(r.AttributeHandle()) if !ok { return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle) } // We don't support write to static value. Pass the request to upper layer. if a == nil { return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrWriteNotPerm) } if e := handleATT(a, s.conn, r, ble.NewResponseWriter(nil)); e != ble.ErrSuccess { return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e) } return []byte{WriteResponseCode} }
// HandleXpcEvent process Device events and asynchronous errors. func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { if err != nil { log.Println("error:", err) return } m := msg(event) args := msg(msg(event).args()) logger.Info("recv", "id", m.id(), "args", fmt.Sprintf("%v", m.args())) switch m.id() { case // Device event evtStateChanged, evtAdvertisingStarted, evtAdvertisingStopped, evtServiceAdded: d.rspc <- args case evtPeripheralDiscovered: if d.advHandler == nil { break } a := &adv{args: m.args(), ad: args.advertisementData()} go d.advHandler(a) case evtConfirmation: // log.Printf("confirmed: %d", args.attributeID()) case evtATTMTU: d.conn(args).SetTxMTU(args.attMTU()) case evtSleveConnectionComplete: // remote peripheral is connected. fallthrough case evtMasterConnectionComplete: // remote central is connected. // Could be LEConnectionComplete or LEConnectionUpdateComplete. c := d.conn(args) c.connInterval = args.connectionInterval() c.connLatency = args.connectionLatency() c.supervisionTimeout = args.supervisionTimeout() case evtReadRequest: aid := args.attributeID() char := d.chars[aid] v := char.Value if v == nil { c := d.conn(args) req := ble.NewRequest(c, nil, args.offset()) buf := bytes.NewBuffer(make([]byte, 0, c.txMTU-1)) rsp := ble.NewResponseWriter(buf) char.ReadHandler.ServeRead(req, rsp) v = buf.Bytes() } d.sendCmd(d.pm, 13, xpc.Dict{ "kCBMsgArgAttributeID": aid, "kCBMsgArgData": v, "kCBMsgArgTransactionID": args.transactionID(), "kCBMsgArgResult": 0, }) case evtWriteRequest: for _, xxw := range args.attWrites() { xw := msg(xxw.(xpc.Dict)) aid := xw.attributeID() char := d.chars[aid] req := ble.NewRequest(d.conn(args), xw.data(), xw.offset()) char.WriteHandler.ServeWrite(req, nil) if xw.ignoreResponse() == 1 { continue } d.sendCmd(d.pm, 13, xpc.Dict{ "kCBMsgArgAttributeID": aid, "kCBMsgArgData": nil, "kCBMsgArgTransactionID": args.transactionID(), "kCBMsgArgResult": 0, }) } case evtSubscribe: // characteristic is subscribed by remote central. d.conn(args).subscribed(d.chars[args.attributeID()]) case evtUnubscribe: // characteristic is unsubscribed by remote central. d.conn(args).unsubscribed(d.chars[args.attributeID()]) case evtPeripheralConnected: d.chConn <- d.conn(args) case evtPeripheralDisconnected: c := d.conn(args) select { case c.rspc <- m: // Canceled by local central synchronously default: // Canceled by remote peripheral asynchronously. } delete(d.conns, c.RemoteAddr().String()) close(c.done) case evtCharacteristicRead: // Notification c := d.conn(args) if args.isNotification() != 0 { sub := c.subs[uint16(args.characteristicHandle())] if sub == nil { log.Printf("notified by unsubscribed handle") // FIXME: should terminate the connection? } else { sub.fn(args.data()) } break } c.rspc <- m case // Peripheral events evtRSSIRead, evtServiceDiscovered, evtIncludedServicesDiscovered, evtCharacteristicsDiscovered, evtCharacteristicWritten, evtNotificationValueSet, evtDescriptorsDiscovered, evtDescriptorRead, evtDescriptorWritten: d.conn(args).rspc <- m default: log.Printf("Unhandled event: %#v", event) } }