blob: e36a8f1b127cc43341af0c861136f5b05169c16d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package omp
import (
"fmt"
"github.com/fatih/structs"
"github.com/runtimeco/go-coap"
"github.com/ugorji/go/codec"
"mynewt.apache.org/newtmgr/nmxact/nmcoap"
"mynewt.apache.org/newtmgr/nmxact/nmp"
"mynewt.apache.org/newtmgr/nmxact/nmxutil"
)
// OIC wrapping adds this many bytes to an NMP message. Calculated by
// comparing an OMP message with its NMP equivalent.
const OMP_MSG_OVERHEAD = 13
type OicMsg struct {
Hdr []byte `codec:"_h"`
}
/*
* Not able to install custom decoder for indefinite length objects with the
* codec. So we need to decode the whole response, and then re-encode the
* newtmgr response part.
*/
func DecodeOmp(m coap.Message, rxFilterCb nmcoap.MsgFilter) (nmp.NmpRsp, error) {
// Ignore non-responses.
if m.Code() == coap.GET || m.Code() == coap.PUT || m.Code() == coap.POST ||
m.Code() == coap.DELETE {
return nil, nil
}
if rxFilterCb != nil {
var err error
m, err = rxFilterCb(m)
if err != nil {
return nil, err
}
}
if m.Code() != coap.Created && m.Code() != coap.Deleted &&
m.Code() != coap.Valid && m.Code() != coap.Changed &&
m.Code() != coap.Content {
return nil, fmt.Errorf(
"OMP response specifies unexpected code: %d (%s)", int(m.Code()),
m.Code().String())
}
var om OicMsg
err := codec.NewDecoderBytes(m.Payload(), new(codec.CborHandle)).Decode(&om)
if err != nil {
return nil, fmt.Errorf("Invalid incoming cbor: %s", err.Error())
}
if om.Hdr == nil {
return nil, fmt.Errorf("Invalid incoming OMP response; NMP header" +
"missing")
}
hdr, err := nmp.DecodeNmpHdr(om.Hdr)
if err != nil {
return nil, err
}
rsp, err := nmp.DecodeRspBody(hdr, m.Payload())
if err != nil {
return nil, fmt.Errorf("Error decoding OMP response: %s", err.Error())
}
if rsp == nil {
return nil, nil
}
return rsp, nil
}
type encodeRecord struct {
m coap.Message
hdrBytes []byte
fieldMap map[string]interface{}
}
func encodeOmpBase(txFilterCb nmcoap.MsgFilter, isTcp bool, nmr *nmp.NmpMsg) (encodeRecord, error) {
er := encodeRecord{}
mp := coap.MessageParams{
Type: coap.Confirmable,
Code: coap.PUT,
Token: nmxutil.SeqToToken(nmr.Hdr.Seq),
}
if isTcp {
er.m = coap.NewTcpMessage(mp)
} else {
er.m = coap.NewDgramMessage(mp)
}
er.m.SetPathString(nmxutil.OmpRes)
payload := []byte{}
enc := codec.NewEncoderBytes(&payload, new(codec.CborHandle))
// Convert request struct to map, use "codec" tag which is compatible with "structs"
s := structs.New(nmr.Body)
s.TagName = "codec"
er.fieldMap = s.Map()
// Add the NMP header to the OMP response map.
er.hdrBytes = nmr.Hdr.Bytes()
er.fieldMap["_h"] = er.hdrBytes
if err := enc.Encode(er.fieldMap); err != nil {
return er, err
}
er.m.SetPayload(payload)
if txFilterCb != nil {
var err error
er.m, err = txFilterCb(er.m)
if err != nil {
return er, err
}
}
return er, nil
}
func EncodeOmpTcp(txFilterCb nmcoap.MsgFilter, nmr *nmp.NmpMsg) ([]byte, error) {
er, err := encodeOmpBase(txFilterCb, true, nmr)
if err != nil {
return nil, err
}
data, err := er.m.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("Failed to encode: %s\n", err.Error())
}
return data, nil
}
func EncodeOmpDgram(txFilterCb nmcoap.MsgFilter, nmr *nmp.NmpMsg) ([]byte, error) {
er, err := encodeOmpBase(txFilterCb, false, nmr)
if err != nil {
return nil, err
}
data, err := er.m.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("Failed to encode: %s\n", err.Error())
}
return data, nil
}