1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-26 13:48:53 +08:00
2018-05-10 23:53:25 +02:00

133 lines
2.9 KiB
Go

package api
import (
"context"
"fmt"
"net"
"strings"
"time"
mux "github.com/dereulenspiegel/coap-mux"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/coap"
gocoap "github.com/dustin/go-coap"
)
const (
chanID = "id"
keyHeader = "key"
)
func authKey(opt interface{}) (string, error) {
val, ok := opt.(string)
if !ok {
return "", errBadRequest
}
arr := strings.Split(val, "=")
if len(arr) != 2 || strings.ToLower(arr[0]) != keyHeader {
return "", errBadOption
}
return arr[1], nil
}
func authorize(msg *gocoap.Message, res *gocoap.Message, cid string) (publisher *mainflux.Identity, err error) {
// Device Key is passed as Uri-Query parameter, which option ID is 15 (0xf).
key, err := authKey(msg.Option(gocoap.URIQuery))
if err != nil {
switch err {
case errBadOption:
res.Code = gocoap.BadOption
case errBadRequest:
res.Code = gocoap.BadRequest
}
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
publisher, err = auth.CanAccess(ctx, &mainflux.AccessReq{key, cid})
if err != nil {
res.Code = gocoap.Unauthorized
}
return
}
func serve(svc coap.Service, conn *net.UDPConn, data []byte, addr *net.UDPAddr, rh gocoap.Handler) {
msg, err := gocoap.ParseMessage(data)
if err != nil {
return
}
res := &gocoap.Message{
Type: gocoap.NonConfirmable,
Code: gocoap.Content,
MessageID: msg.MessageID,
Token: msg.Token,
Payload: []byte{},
}
switch msg.Type {
case gocoap.Reset:
if len(msg.Payload) != 0 {
res.Code = gocoap.BadRequest
break
}
cid := mux.Var(&msg, chanID)
res.Type = gocoap.Acknowledgement
publisher, err := authorize(&msg, res, cid)
if err != nil {
res.Code = gocoap.Unauthorized
break
}
id := fmt.Sprintf("%s-%x", publisher, msg.Token)
svc.RemoveTimeout(id)
svc.Unsubscribe(id)
case gocoap.Acknowledgement:
cid := mux.Var(&msg, chanID)
res.Type = gocoap.Acknowledgement
publisher, err := authorize(&msg, res, cid)
if err != nil {
res.Code = gocoap.Unauthorized
break
}
id := fmt.Sprintf("%s-%x", publisher, msg.Token)
svc.RemoveTimeout(id)
default:
res = rh.ServeCOAP(conn, addr, &msg)
}
if res != nil && msg.IsConfirmable() {
gocoap.Transmit(conn, addr, *res)
}
}
// ListenAndServe binds to the given address and serve requests forever.
func ListenAndServe(svc coap.Service, csc mainflux.ClientsServiceClient, addr string, rh gocoap.Handler) error {
auth = csc
uaddr, err := net.ResolveUDPAddr(network, addr)
if err != nil {
return err
}
conn, err := net.ListenUDP(network, uaddr)
if err != nil {
return err
}
buf := make([]byte, maxPktLen)
for {
nr, addr, err := conn.ReadFromUDP(buf)
if err != nil {
if neterr, ok := err.(net.Error); ok && (neterr.Temporary() || neterr.Timeout()) {
time.Sleep(5 * time.Millisecond)
continue
}
return err
}
tmp := make([]byte, nr)
copy(tmp, buf)
go serve(svc, conn, tmp, addr, rh)
}
}