feat: initial api for cwmp
This commit is contained in:
parent
5487ede44e
commit
406d62550f
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"oktopUSP/backend/services/acs/internal/bridge"
|
||||||
"oktopUSP/backend/services/acs/internal/config"
|
"oktopUSP/backend/services/acs/internal/config"
|
||||||
"oktopUSP/backend/services/acs/internal/nats"
|
"oktopUSP/backend/services/acs/internal/nats"
|
||||||
"oktopUSP/backend/services/acs/internal/server"
|
"oktopUSP/backend/services/acs/internal/server"
|
||||||
|
|
@ -15,5 +16,12 @@ func main() {
|
||||||
|
|
||||||
h := handler.NewHandler(natsActions.Publish, natsActions.Subscribe)
|
h := handler.NewHandler(natsActions.Publish, natsActions.Subscribe)
|
||||||
|
|
||||||
|
b := bridge.NewBridge(
|
||||||
|
natsActions.Publish,
|
||||||
|
natsActions.Subscribe,
|
||||||
|
h,
|
||||||
|
)
|
||||||
|
b.StartBridge()
|
||||||
|
|
||||||
server.Run(c.Acs, natsActions, h)
|
server.Run(c.Acs, natsActions, h)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ module oktopUSP/backend/services/acs
|
||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/nats-io/nats.go v1.34.1
|
github.com/nats-io/nats.go v1.34.1
|
||||||
github.com/oleiade/lane v1.0.1
|
github.com/oleiade/lane v1.0.1
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -36,37 +35,37 @@ func Auth(username string, password string, uri string) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if resp.StatusCode == 401 {
|
// if resp.StatusCode == 401 {
|
||||||
var authorization map[string]string = DigestAuthParams(resp)
|
// var authorization map[string]string = DigestAuthParams(resp)
|
||||||
realmHeader := authorization["realm"]
|
// realmHeader := authorization["realm"]
|
||||||
qopHeader := authorization["qop"]
|
// qopHeader := authorization["qop"]
|
||||||
nonceHeader := authorization["nonce"]
|
// nonceHeader := authorization["nonce"]
|
||||||
opaqueHeader := authorization["opaque"]
|
// opaqueHeader := authorization["opaque"]
|
||||||
realm := realmHeader
|
// realm := realmHeader
|
||||||
// A1
|
// // A1
|
||||||
h := md5.New()
|
// h := md5.New()
|
||||||
A1 := fmt.Sprintf("%s:%s:%s", username, realm, password)
|
// A1 := fmt.Sprintf("%s:%s:%s", username, realm, password)
|
||||||
io.WriteString(h, A1)
|
// io.WriteString(h, A1)
|
||||||
HA1 := fmt.Sprintf("%x", h.Sum(nil))
|
// HA1 := fmt.Sprintf("%x", h.Sum(nil))
|
||||||
|
|
||||||
// A2
|
// // A2
|
||||||
h = md5.New()
|
// h = md5.New()
|
||||||
A2 := fmt.Sprintf("GET:%s", "/auth")
|
// A2 := fmt.Sprintf("GET:%s", "/auth")
|
||||||
io.WriteString(h, A2)
|
// io.WriteString(h, A2)
|
||||||
HA2 := fmt.Sprintf("%x", h.Sum(nil))
|
// HA2 := fmt.Sprintf("%x", h.Sum(nil))
|
||||||
|
|
||||||
// response
|
// // response
|
||||||
cnonce := RandomKey()
|
// cnonce := RandomKey()
|
||||||
response := H(strings.Join([]string{HA1, nonceHeader, "00000001", cnonce, qopHeader, HA2}, ":"))
|
// response := H(strings.Join([]string{HA1, nonceHeader, "00000001", cnonce, qopHeader, HA2}, ":"))
|
||||||
|
|
||||||
// now make header
|
// // now make header
|
||||||
AuthHeader := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=00000001, qop=%s, response="%s", opaque="%s", algorithm=MD5`,
|
// AuthHeader := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=00000001, qop=%s, response="%s", opaque="%s", algorithm=MD5`,
|
||||||
username, realmHeader, nonceHeader, "/auth", cnonce, qopHeader, response, opaqueHeader)
|
// username, realmHeader, nonceHeader, "/auth", cnonce, qopHeader, response, opaqueHeader)
|
||||||
req.Header.Set("Authorization", AuthHeader)
|
// req.Header.Set("Authorization", AuthHeader)
|
||||||
resp, err = client.Do(req)
|
// resp, err = client.Do(req)
|
||||||
} else {
|
// } else {
|
||||||
return false, fmt.Errorf("response status code should have been 401, it was %v", resp.StatusCode)
|
// return false, fmt.Errorf("response status code should have been 401, it was %v", resp.StatusCode)
|
||||||
}
|
// }
|
||||||
return resp.StatusCode == 200, err
|
return resp.StatusCode == 200, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
112
backend/services/acs/internal/bridge/bridge.go
Normal file
112
backend/services/acs/internal/bridge/bridge.go
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"oktopUSP/backend/services/acs/internal/server/handler"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bridge struct {
|
||||||
|
pub func(string, []byte) error
|
||||||
|
sub func(string, func(*nats.Msg)) error
|
||||||
|
cpes map[string]handler.CPE
|
||||||
|
h *handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
type msgAnswer struct {
|
||||||
|
Code int
|
||||||
|
Msg any
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEVICE_ANSWER_TIMEOUT = 5 * time.Second
|
||||||
|
|
||||||
|
func NewBridge(
|
||||||
|
pub func(string, []byte) error,
|
||||||
|
sub func(string, func(*nats.Msg)) error,
|
||||||
|
h *handler.Handler,
|
||||||
|
) *Bridge {
|
||||||
|
return &Bridge{
|
||||||
|
pub: pub,
|
||||||
|
sub: sub,
|
||||||
|
cpes: h.Cpes,
|
||||||
|
h: h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) StartBridge() {
|
||||||
|
b.sub(handler.NATS_CWMP_ADAPTER_SUBJECT_PREFIX+"*.api", func(msg *nats.Msg) {
|
||||||
|
log.Printf("Received message: %s", string(msg.Data))
|
||||||
|
log.Printf("Subject: %s", msg.Subject)
|
||||||
|
log.Printf("Reply: %s", msg.Reply)
|
||||||
|
|
||||||
|
device := getDeviceFromSubject(msg.Subject)
|
||||||
|
cpe, ok := b.cpes[device]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Device %s not found", device)
|
||||||
|
respondMsg(msg.Respond, http.StatusNotFound, "Device not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cpe.Queue.Size() > 0 {
|
||||||
|
log.Printf("Device %s is busy", device)
|
||||||
|
respondMsg(msg.Respond, http.StatusConflict, "Device is busy")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceAnswer := make(chan []byte)
|
||||||
|
defer close(deviceAnswer)
|
||||||
|
|
||||||
|
cpe.Queue.Enqueue(handler.Request{ //TODO: pass user and password too
|
||||||
|
Id: uuid.NewString(),
|
||||||
|
CwmpMsg: msg.Data,
|
||||||
|
Callback: deviceAnswer,
|
||||||
|
})
|
||||||
|
|
||||||
|
err := b.h.ConnectionRequest(cpe)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to do connection request", err)
|
||||||
|
cpe.Queue.Dequeue()
|
||||||
|
respondMsg(msg.Respond, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//req := cpe.Queue.Dequeue().(handler.Request)
|
||||||
|
//cpe.Waiting = &req
|
||||||
|
|
||||||
|
select {
|
||||||
|
case response := <-deviceAnswer:
|
||||||
|
log.Println("Received response from device: ", string(response))
|
||||||
|
respondMsg(msg.Respond, http.StatusOK, response)
|
||||||
|
case <-time.After(DEVICE_ANSWER_TIMEOUT):
|
||||||
|
log.Println("Device response timed out")
|
||||||
|
respondMsg(msg.Respond, http.StatusRequestTimeout, "Request timeout")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func respondMsg(respond func(data []byte) error, code int, msgData any) {
|
||||||
|
|
||||||
|
msg, err := json.Marshal(msgAnswer{
|
||||||
|
Code: code,
|
||||||
|
Msg: msgData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to marshal message: %q", err)
|
||||||
|
respond([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respond(msg)
|
||||||
|
//log.Println("Responded with message: ", string(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeviceFromSubject(subject string) string {
|
||||||
|
paths := strings.Split(subject, ".")
|
||||||
|
device := paths[len(paths)-2]
|
||||||
|
return device
|
||||||
|
}
|
||||||
164
backend/services/acs/internal/server/handler/cwmp.go
Normal file
164
backend/services/acs/internal/server/handler/cwmp.go
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"oktopUSP/backend/services/acs/internal/auth"
|
||||||
|
"oktopUSP/backend/services/acs/internal/cwmp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/oleiade/lane"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *Handler) CwmpHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
log.Printf("--> Connection from %s", r.RemoteAddr)
|
||||||
|
|
||||||
|
defer r.Body.Close()
|
||||||
|
defer log.Printf("<-- Connection from %s closed", r.RemoteAddr)
|
||||||
|
|
||||||
|
tmp, _ := ioutil.ReadAll(r.Body)
|
||||||
|
body := string(tmp)
|
||||||
|
|
||||||
|
var envelope cwmp.SoapEnvelope
|
||||||
|
xml.Unmarshal(tmp, &envelope)
|
||||||
|
|
||||||
|
messageType := envelope.Body.CWMPMessage.XMLName.Local
|
||||||
|
log.Println("messageType: ", messageType)
|
||||||
|
|
||||||
|
var cpe CPE
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
w.Header().Set("Server", "Oktopus "+Version)
|
||||||
|
|
||||||
|
if messageType != "Inform" {
|
||||||
|
if cookie, err := r.Cookie("oktopus"); err == nil {
|
||||||
|
if cpe, exists = h.Cpes[cookie.Value]; !exists {
|
||||||
|
log.Printf("CPE with serial number %s not found", cookie.Value)
|
||||||
|
}
|
||||||
|
log.Printf("CPE with serial number %s found", cookie.Value)
|
||||||
|
} else {
|
||||||
|
fmt.Println("cookie 'oktopus' missing")
|
||||||
|
w.WriteHeader(401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if messageType == "Inform" {
|
||||||
|
var Inform cwmp.CWMPInform
|
||||||
|
xml.Unmarshal(tmp, &Inform)
|
||||||
|
|
||||||
|
var addr string
|
||||||
|
if r.Header.Get("X-Real-Ip") != "" {
|
||||||
|
addr = r.Header.Get("X-Real-Ip")
|
||||||
|
} else {
|
||||||
|
addr = r.RemoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
sn := Inform.DeviceId.SerialNumber
|
||||||
|
|
||||||
|
if _, exists := h.Cpes[sn]; !exists {
|
||||||
|
log.Println("New device: " + sn)
|
||||||
|
h.Cpes[sn] = CPE{
|
||||||
|
SerialNumber: sn,
|
||||||
|
LastConnection: time.Now().UTC(),
|
||||||
|
SoftwareVersion: Inform.GetSoftwareVersion(),
|
||||||
|
HardwareVersion: Inform.GetHardwareVersion(),
|
||||||
|
ExternalIPAddress: addr,
|
||||||
|
ConnectionRequestURL: Inform.GetConnectionRequest(),
|
||||||
|
OUI: Inform.DeviceId.OUI,
|
||||||
|
Queue: lane.NewQueue(),
|
||||||
|
DataModel: Inform.GetDataModelType(),
|
||||||
|
}
|
||||||
|
go h.handleCpeStatus(sn)
|
||||||
|
h.pub(NATS_CWMP_SUBJECT_PREFIX+sn+".info", tmp)
|
||||||
|
}
|
||||||
|
obj := h.Cpes[sn]
|
||||||
|
cpe := &obj
|
||||||
|
cpe.LastConnection = time.Now().UTC()
|
||||||
|
|
||||||
|
log.Printf("Received an Inform from device %s withEventCodes %s", addr, Inform.GetEvents())
|
||||||
|
|
||||||
|
expiration := time.Now().AddDate(0, 0, 1)
|
||||||
|
|
||||||
|
cookie := http.Cookie{Name: "oktopus", Value: sn, Expires: expiration}
|
||||||
|
http.SetCookie(w, &cookie)
|
||||||
|
data, _ := xml.Marshal(cwmp.InformResponse(envelope.Header.Id))
|
||||||
|
w.Write(data)
|
||||||
|
} else if messageType == "TransferComplete" {
|
||||||
|
|
||||||
|
} else if messageType == "GetRPC" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if len(body) == 0 {
|
||||||
|
log.Println("Got Empty Post")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpe.Waiting != nil {
|
||||||
|
log.Println("CPE was waiting for a response, now received something")
|
||||||
|
var e cwmp.SoapEnvelope
|
||||||
|
xml.Unmarshal([]byte(body), &e)
|
||||||
|
log.Println("Kind of envelope: ", e.KindOf())
|
||||||
|
|
||||||
|
if e.KindOf() == "GetParameterNamesResponse" {
|
||||||
|
// var envelope cwmp.GetParameterNamesResponse
|
||||||
|
// xml.Unmarshal([]byte(body), &envelope)
|
||||||
|
|
||||||
|
// msg := new(NatsSendMessage)
|
||||||
|
// msg.MsgType = "GetParameterNamesResponse"
|
||||||
|
// msg.Data, _ = json.Marshal(envelope)
|
||||||
|
log.Println("Receive GetParameterNamesResponse from CPE:", cpe.SerialNumber)
|
||||||
|
cpe.Waiting.Callback <- tmp
|
||||||
|
|
||||||
|
} else if e.KindOf() == "GetParameterValuesResponse" {
|
||||||
|
var envelope cwmp.GetParameterValuesResponse
|
||||||
|
xml.Unmarshal([]byte(body), &envelope)
|
||||||
|
|
||||||
|
msg := new(NatsSendMessage)
|
||||||
|
msg.MsgType = "GetParameterValuesResponse"
|
||||||
|
msg.Data, _ = json.Marshal(envelope)
|
||||||
|
|
||||||
|
cpe.Waiting.Callback <- tmp
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.Println("Unknown message type")
|
||||||
|
cpe.Waiting.Callback <- tmp
|
||||||
|
}
|
||||||
|
cpe.Waiting = nil
|
||||||
|
} else {
|
||||||
|
log.Println("CPE was not waiting for a response")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("CPE %s Queue size: %d", cpe.SerialNumber, cpe.Queue.Size())
|
||||||
|
|
||||||
|
if cpe.Queue.Size() > 0 {
|
||||||
|
req := cpe.Queue.Dequeue().(Request)
|
||||||
|
cpe.Waiting = &req
|
||||||
|
log.Println("Sending request to CPE:", req.Id)
|
||||||
|
w.Header().Set("Connection", "keep-alive")
|
||||||
|
w.Write(req.CwmpMsg)
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Connection", "close")
|
||||||
|
w.WriteHeader(204)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.Cpes[cpe.SerialNumber] = cpe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) ConnectionRequest(cpe CPE) error {
|
||||||
|
log.Println("--> ConnectionRequest, CPE: ", cpe.SerialNumber)
|
||||||
|
// log.Println("ConnectionRequestURL: ", cpe.ConnectionRequestURL)
|
||||||
|
// log.Println("ConnectionRequestUsername: ", cpe.Username)
|
||||||
|
// log.Println("ConnectionRequestPassword: ", cpe.Password)
|
||||||
|
|
||||||
|
ok, err := auth.Auth("", "", cpe.ConnectionRequestURL)
|
||||||
|
if !ok {
|
||||||
|
log.Println("Error while authenticating to CPE: ", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -2,26 +2,20 @@ package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"oktopUSP/backend/services/acs/internal/cwmp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/oleiade/lane"
|
"github.com/oleiade/lane"
|
||||||
"golang.org/x/net/websocket"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const Version = "1.0.0"
|
const Version = "1.0.0"
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Id string
|
Id string
|
||||||
Websocket *websocket.Conn
|
User string
|
||||||
CwmpMessage string
|
Password string
|
||||||
Callback func(msg *WsSendMessage) error
|
CwmpMsg []byte
|
||||||
|
Callback chan []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type CPE struct {
|
type CPE struct {
|
||||||
|
|
@ -36,7 +30,8 @@ type CPE struct {
|
||||||
HardwareVersion string
|
HardwareVersion string
|
||||||
LastConnection time.Time
|
LastConnection time.Time
|
||||||
DataModel string
|
DataModel string
|
||||||
KeepConnectionOpen bool
|
Username string
|
||||||
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
|
|
@ -48,7 +43,7 @@ type WsMessage struct {
|
||||||
Cmd string
|
Cmd string
|
||||||
}
|
}
|
||||||
|
|
||||||
type WsSendMessage struct {
|
type NatsSendMessage struct {
|
||||||
MsgType string
|
MsgType string
|
||||||
Data json.RawMessage
|
Data json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
@ -60,153 +55,19 @@ type MsgCPEs struct {
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
pub func(string, []byte) error
|
pub func(string, []byte) error
|
||||||
sub func(string, func(*nats.Msg)) error
|
sub func(string, func(*nats.Msg)) error
|
||||||
cpes map[string]CPE
|
Cpes map[string]CPE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
NATS_CWMP_SUBJECT_PREFIX = "cwmp.v1."
|
||||||
|
NATS_CWMP_ADAPTER_SUBJECT_PREFIX = "cwmp-adapter.v1."
|
||||||
|
NATS_ADAPTER_SUBJECT_PREFIX = "adapter.v1."
|
||||||
|
)
|
||||||
|
|
||||||
func NewHandler(pub func(string, []byte) error, sub func(string, func(*nats.Msg)) error) *Handler {
|
func NewHandler(pub func(string, []byte) error, sub func(string, func(*nats.Msg)) error) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
pub: pub,
|
pub: pub,
|
||||||
sub: sub,
|
sub: sub,
|
||||||
cpes: make(map[string]CPE),
|
Cpes: make(map[string]CPE),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) CwmpHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
log.Printf("--> Connection from %s", r.RemoteAddr)
|
|
||||||
|
|
||||||
defer r.Body.Close()
|
|
||||||
defer log.Printf("<-- Connection from %s closed", r.RemoteAddr)
|
|
||||||
|
|
||||||
tmp, _ := ioutil.ReadAll(r.Body)
|
|
||||||
body := string(tmp)
|
|
||||||
len := len(body)
|
|
||||||
|
|
||||||
var envelope cwmp.SoapEnvelope
|
|
||||||
xml.Unmarshal(tmp, &envelope)
|
|
||||||
|
|
||||||
messageType := envelope.Body.CWMPMessage.XMLName.Local
|
|
||||||
|
|
||||||
var cpe *CPE
|
|
||||||
|
|
||||||
w.Header().Set("Server", "Oktopus "+Version)
|
|
||||||
|
|
||||||
if messageType != "Inform" {
|
|
||||||
if cookie, err := r.Cookie("oktopus"); err == nil {
|
|
||||||
if _, exists := h.cpes[cookie.Value]; !exists {
|
|
||||||
log.Printf("CPE with serial number %s not found", cookie.Value)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Println("cookie 'oktopus' missing")
|
|
||||||
w.WriteHeader(401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageType == "Inform" {
|
|
||||||
var Inform cwmp.CWMPInform
|
|
||||||
xml.Unmarshal(tmp, &Inform)
|
|
||||||
|
|
||||||
var addr string
|
|
||||||
if r.Header.Get("X-Real-Ip") != "" {
|
|
||||||
addr = r.Header.Get("X-Real-Ip")
|
|
||||||
} else {
|
|
||||||
addr = r.RemoteAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
sn := Inform.DeviceId.SerialNumber
|
|
||||||
|
|
||||||
if _, exists := h.cpes[sn]; !exists {
|
|
||||||
fmt.Println("New device: " + sn)
|
|
||||||
h.cpes[sn] = CPE{
|
|
||||||
SerialNumber: sn,
|
|
||||||
LastConnection: time.Now().UTC(),
|
|
||||||
SoftwareVersion: Inform.GetSoftwareVersion(),
|
|
||||||
HardwareVersion: Inform.GetHardwareVersion(),
|
|
||||||
ExternalIPAddress: addr,
|
|
||||||
ConnectionRequestURL: Inform.GetConnectionRequest(),
|
|
||||||
OUI: Inform.DeviceId.OUI,
|
|
||||||
Queue: lane.NewQueue(),
|
|
||||||
DataModel: Inform.GetDataModelType(),
|
|
||||||
KeepConnectionOpen: false,
|
|
||||||
}
|
|
||||||
go h.handleCpeStatus(sn)
|
|
||||||
h.pub("cwmp.v1."+sn+".info", tmp)
|
|
||||||
}
|
|
||||||
obj := h.cpes[sn]
|
|
||||||
cpe := &obj
|
|
||||||
cpe.LastConnection = time.Now().UTC()
|
|
||||||
|
|
||||||
log.Printf("Received an Inform from %s (%d bytes) with SerialNumber %s and EventCodes %s", addr, len, sn, Inform.GetEvents())
|
|
||||||
|
|
||||||
expiration := time.Now().AddDate(0, 0, 1)
|
|
||||||
|
|
||||||
cookie := http.Cookie{Name: "oktopus", Value: sn, Expires: expiration}
|
|
||||||
http.SetCookie(w, &cookie)
|
|
||||||
} else if messageType == "TransferComplete" {
|
|
||||||
|
|
||||||
} else if messageType == "GetRPC" {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if len == 0 {
|
|
||||||
log.Printf("Got Empty Post")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cpe.Waiting != nil {
|
|
||||||
var e cwmp.SoapEnvelope
|
|
||||||
xml.Unmarshal([]byte(body), &e)
|
|
||||||
|
|
||||||
if e.KindOf() == "GetParameterNamesResponse" {
|
|
||||||
var envelope cwmp.GetParameterNamesResponse
|
|
||||||
xml.Unmarshal([]byte(body), &envelope)
|
|
||||||
|
|
||||||
msg := new(WsSendMessage)
|
|
||||||
msg.MsgType = "GetParameterNamesResponse"
|
|
||||||
msg.Data, _ = json.Marshal(envelope)
|
|
||||||
|
|
||||||
cpe.Waiting.Callback(msg)
|
|
||||||
// if err := websocket.JSON.Send(cpe.Waiting.Websocket, msg); err != nil {
|
|
||||||
// fmt.Println("error while sending back answer:", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
} else if e.KindOf() == "GetParameterValuesResponse" {
|
|
||||||
var envelope cwmp.GetParameterValuesResponse
|
|
||||||
xml.Unmarshal([]byte(body), &envelope)
|
|
||||||
|
|
||||||
msg := new(WsSendMessage)
|
|
||||||
msg.MsgType = "GetParameterValuesResponse"
|
|
||||||
msg.Data, _ = json.Marshal(envelope)
|
|
||||||
|
|
||||||
cpe.Waiting.Callback(msg)
|
|
||||||
// if err := websocket.JSON.Send(cpe.Waiting.Websocket, msg); err != nil {
|
|
||||||
// fmt.Println("error while sending back answer:", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
} else {
|
|
||||||
msg := new(WsMessage)
|
|
||||||
msg.Cmd = body
|
|
||||||
|
|
||||||
if err := websocket.JSON.Send(cpe.Waiting.Websocket, msg); err != nil {
|
|
||||||
fmt.Println("error while sending back answer:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cpe.Waiting = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Got Empty Post or a Response. Now check for any event to send, otherwise 204
|
|
||||||
if cpe.Queue.Size() > 0 {
|
|
||||||
req := cpe.Queue.Dequeue().(Request)
|
|
||||||
// fmt.Println("sending "+req.CwmpMessage)
|
|
||||||
fmt.Fprintf(w, req.CwmpMessage)
|
|
||||||
cpe.Waiting = &req
|
|
||||||
} else {
|
|
||||||
if cpe.KeepConnectionOpen {
|
|
||||||
fmt.Println("I'm keeping connection open")
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(204)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ import (
|
||||||
|
|
||||||
// TODO: make these consts dynamic via config
|
// TODO: make these consts dynamic via config
|
||||||
const (
|
const (
|
||||||
CHECK_STATUS_INTERVAL = 5 * time.Second
|
CHECK_STATUS_INTERVAL = 10 * time.Second
|
||||||
KEEP_ALIVE_INTERVAL = 10 * time.Second
|
KEEP_ALIVE_INTERVAL = 600 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) handleCpeStatus(cpe string) {
|
func (h *Handler) handleCpeStatus(cpe string) {
|
||||||
for {
|
for {
|
||||||
if time.Since(h.cpes[cpe].LastConnection) > KEEP_ALIVE_INTERVAL {
|
if time.Since(h.Cpes[cpe].LastConnection) > KEEP_ALIVE_INTERVAL {
|
||||||
delete(h.cpes, cpe)
|
delete(h.Cpes, cpe)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(CHECK_STATUS_INTERVAL)
|
time.Sleep(CHECK_STATUS_INTERVAL)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import (
|
||||||
func Run(c config.Acs, natsActions nats.NatsActions, h *handler.Handler) {
|
func Run(c config.Acs, natsActions nats.NatsActions, h *handler.Handler) {
|
||||||
|
|
||||||
http.HandleFunc(c.Route, h.CwmpHandler)
|
http.HandleFunc(c.Route, h.CwmpHandler)
|
||||||
|
|
||||||
log.Printf("ACS running at %s%s", c.Port, c.Route)
|
log.Printf("ACS running at %s%s", c.Port, c.Route)
|
||||||
|
|
||||||
err := http.ListenAndServe(c.Port, nil)
|
err := http.ListenAndServe(c.Port, nil)
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ func (a *Api) StartApi() {
|
||||||
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
|
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
|
||||||
iot := r.PathPrefix("/api/device").Subrouter()
|
iot := r.PathPrefix("/api/device").Subrouter()
|
||||||
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "PUT", "DELETE")
|
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "PUT", "DELETE")
|
||||||
|
iot.HandleFunc("/cwmp/{sn}/getParameterNames", a.cwmpGetParameterNamesMsg).Methods("GET", "PUT", "DELETE")
|
||||||
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
|
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
|
||||||
iot.HandleFunc("/{id}", a.retrieveDevices).Methods("GET")
|
iot.HandleFunc("/{id}", a.retrieveDevices).Methods("GET")
|
||||||
iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT")
|
||||||
|
|
|
||||||
48
backend/services/controller/internal/api/cwmp.go
Normal file
48
backend/services/controller/internal/api/cwmp.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/leandrofars/oktopus/internal/bridge"
|
||||||
|
"github.com/leandrofars/oktopus/internal/cwmp"
|
||||||
|
"github.com/leandrofars/oktopus/internal/nats"
|
||||||
|
"github.com/leandrofars/oktopus/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *Api) cwmpGetParameterNamesMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sn := getSerialNumberFromRequest(r)
|
||||||
|
|
||||||
|
payload, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(utils.Marshall(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := bridge.NatsCwmpInteraction(
|
||||||
|
nats.NATS_CWMP_ADAPTER_SUBJECT_PREFIX+sn+".api",
|
||||||
|
payload,
|
||||||
|
w,
|
||||||
|
a.nc,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var response cwmp.GetParameterNamesResponse
|
||||||
|
err = xml.Unmarshal(data, &response)
|
||||||
|
if err != nil {
|
||||||
|
err = json.Unmarshal(data, &response)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write(utils.Marshall(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
@ -204,3 +204,45 @@ func NatsReqWithoutHttpSet[T entity.DataType](
|
||||||
|
|
||||||
return answer, nil
|
return answer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NatsCwmpInteraction(
|
||||||
|
subj string,
|
||||||
|
body []byte,
|
||||||
|
w http.ResponseWriter,
|
||||||
|
nc *nats.Conn,
|
||||||
|
) ([]byte, error) {
|
||||||
|
|
||||||
|
log.Println("Sending cwmp message")
|
||||||
|
log.Println("Subject: ", subj)
|
||||||
|
|
||||||
|
var answer entity.MsgAnswer[[]byte]
|
||||||
|
|
||||||
|
msg, err := nc.Request(subj, body, local.NATS_REQUEST_TIMEOUT)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write(utils.Marshall("Error to communicate with nats: " + err.Error()))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(msg.Data, &answer)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
var errMsg *entity.MsgAnswer[*string]
|
||||||
|
err = json.Unmarshal(msg.Data, &errMsg)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Bad answer message formatting: ", err.Error())
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write(msg.Data)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Error message received, msg: %s, code: %d", *errMsg.Msg, errMsg.Code)
|
||||||
|
w.WriteHeader(errMsg.Code)
|
||||||
|
w.Write(utils.Marshall(*errMsg.Msg))
|
||||||
|
return nil, errNatsMsgReceivedWithErrorData
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer.Msg, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
542
backend/services/controller/internal/cwmp/cwmp.go
Normal file
542
backend/services/controller/internal/cwmp/cwmp.go
Normal file
|
|
@ -0,0 +1,542 @@
|
||||||
|
package cwmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SoapEnvelope struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
Header SoapHeader
|
||||||
|
Body SoapBody
|
||||||
|
}
|
||||||
|
|
||||||
|
type SoapHeader struct {
|
||||||
|
Id string `xml:"ID"`
|
||||||
|
}
|
||||||
|
type SoapBody struct {
|
||||||
|
CWMPMessage CWMPMessage `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CWMPMessage struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventStruct struct {
|
||||||
|
EventCode string
|
||||||
|
CommandKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParameterValueStruct struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParameterInfoStruct struct {
|
||||||
|
Name string
|
||||||
|
Writable string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetParameterValues_ struct {
|
||||||
|
ParameterList []ParameterValueStruct `xml:"Body>SetParameterValues>ParameterList>ParameterValueStruct"`
|
||||||
|
ParameterKey string `xml:"Body>SetParameterValues>ParameterKey>string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetParameterValues_ struct {
|
||||||
|
ParameterNames []string `xml:"Body>GetParameterValues>ParameterNames>string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetParameterNames_ struct {
|
||||||
|
ParameterPath []string `xml:"Body>GetParameterNames>ParameterPath"`
|
||||||
|
NextLevel string `xml:"Body>GetParameterNames>NextLevel"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetParameterValuesResponse struct {
|
||||||
|
ParameterList []ParameterValueStruct `xml:"Body>GetParameterValuesResponse>ParameterList>ParameterValueStruct"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetParameterNamesResponse struct {
|
||||||
|
ParameterList []ParameterInfoStruct `xml:"Body>GetParameterNamesResponse>ParameterList>ParameterInfoStruct"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CWMPInform struct {
|
||||||
|
DeviceId DeviceID `xml:"Body>Inform>DeviceId"`
|
||||||
|
Events []EventStruct `xml:"Body>Inform>Event>EventStruct"`
|
||||||
|
ParameterList []ParameterValueStruct `xml:"Body>Inform>ParameterList>ParameterValueStruct"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SoapEnvelope) KindOf() string {
|
||||||
|
return s.Body.CWMPMessage.XMLName.Local
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *CWMPInform) GetEvents() string {
|
||||||
|
res := ""
|
||||||
|
for idx := range i.Events {
|
||||||
|
res += i.Events[idx].EventCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *CWMPInform) GetConnectionRequest() string {
|
||||||
|
for idx := range i.ParameterList {
|
||||||
|
// valid condition for both tr98 and tr181
|
||||||
|
if strings.HasSuffix(i.ParameterList[idx].Name, "Device.ManagementServer.ConnectionRequestURL") {
|
||||||
|
return i.ParameterList[idx].Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *CWMPInform) GetSoftwareVersion() string {
|
||||||
|
for idx := range i.ParameterList {
|
||||||
|
if strings.HasSuffix(i.ParameterList[idx].Name, "Device.DeviceInfo.SoftwareVersion") {
|
||||||
|
return i.ParameterList[idx].Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *CWMPInform) GetHardwareVersion() string {
|
||||||
|
for idx := range i.ParameterList {
|
||||||
|
if strings.HasSuffix(i.ParameterList[idx].Name, "Device.DeviceInfo.HardwareVersion") {
|
||||||
|
return i.ParameterList[idx].Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *CWMPInform) GetDataModelType() string {
|
||||||
|
if strings.HasPrefix(i.ParameterList[0].Name, "InternetGatewayDevice") {
|
||||||
|
return "TR098"
|
||||||
|
} else if strings.HasPrefix(i.ParameterList[0].Name, "Device") {
|
||||||
|
return "TR181"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceID struct {
|
||||||
|
Manufacturer string
|
||||||
|
OUI string
|
||||||
|
SerialNumber string
|
||||||
|
ProductClass string
|
||||||
|
}
|
||||||
|
|
||||||
|
func InformResponse(mustUnderstand string) string {
|
||||||
|
mustUnderstandHeader := ""
|
||||||
|
if mustUnderstand != "" {
|
||||||
|
mustUnderstandHeader = `<cwmp:ID soap:mustUnderstand="1">` + mustUnderstand + `</cwmp:ID>`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header>` + mustUnderstandHeader + `</soap:Header>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:InformResponse>
|
||||||
|
<MaxEnvelopes>1</MaxEnvelopes>
|
||||||
|
</cwmp:InformResponse>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetParameterValues(leaf string) string {
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:GetParameterValues>
|
||||||
|
<ParameterNames>
|
||||||
|
<string>` + leaf + `</string>
|
||||||
|
</ParameterNames>
|
||||||
|
</cwmp:GetParameterValues>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetParameterMultiValues(leaves []string) string {
|
||||||
|
msg := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:GetParameterValues>
|
||||||
|
<ParameterNames>`
|
||||||
|
|
||||||
|
for idx := range leaves {
|
||||||
|
msg += `<string>` + leaves[idx] + `</string>`
|
||||||
|
|
||||||
|
}
|
||||||
|
msg += `</ParameterNames>
|
||||||
|
</cwmp:GetParameterValues>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetParameterValues(leaf string, value string) string {
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:SetParameterValues>
|
||||||
|
<ParameterList soapenc:arrayType="cwmp:ParameterValueStruct[1]">
|
||||||
|
<ParameterValueStruct>
|
||||||
|
<Name>` + leaf + `</Name>
|
||||||
|
<Value>` + value + `</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
</ParameterList>
|
||||||
|
<ParameterKey>LC1309` + randToken() + `</ParameterKey>
|
||||||
|
</cwmp:SetParameterValues>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func randToken() string {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
rand.Read(b)
|
||||||
|
return fmt.Sprintf("%x", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetParameterMultiValues(data map[string]string) string {
|
||||||
|
msg := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:SetParameterValues>
|
||||||
|
<ParameterList soapenc:arrayType="cwmp:ParameterValueStruct[` + string(len(data)) + `]">`
|
||||||
|
|
||||||
|
for key, value := range data {
|
||||||
|
msg += `<ParameterValueStruct>
|
||||||
|
<Name>` + key + `</Name>
|
||||||
|
<Value>` + value + `</Value>
|
||||||
|
</ParameterValueStruct>`
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += `</ParameterList>
|
||||||
|
<ParameterKey>LC1309` + randToken() + `</ParameterKey>
|
||||||
|
</cwmp:SetParameterValues>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetParameterNames(leaf string, nextlevel int) string {
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:GetParameterNames>
|
||||||
|
<ParameterPath>` + leaf + `</ParameterPath>
|
||||||
|
<NextLevel>` + strconv.Itoa(nextlevel) + `</NextLevel>
|
||||||
|
</cwmp:GetParameterNames>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FactoryReset() string {
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:FactoryReset/>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Download(filetype, url, username, password, filesize string) string {
|
||||||
|
// 3 Vendor Configuration File
|
||||||
|
// 1 Firmware Upgrade Image
|
||||||
|
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:Download>
|
||||||
|
<CommandKey>MSDWK</CommandKey>
|
||||||
|
<FileType>` + filetype + `</FileType>
|
||||||
|
<URL>` + url + `</URL>
|
||||||
|
<Username>` + username + `</Username>
|
||||||
|
<Password>` + password + `</Password>
|
||||||
|
<FileSize>` + filesize + `</FileSize>
|
||||||
|
<TargetFileName></TargetFileName>
|
||||||
|
<DelaySeconds>0</DelaySeconds>
|
||||||
|
<SuccessURL></SuccessURL>
|
||||||
|
<FailureURL></FailureURL>
|
||||||
|
</cwmp:Download>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CancelTransfer() string {
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:CancelTransfer>
|
||||||
|
<CommandKey></CommandKey>
|
||||||
|
<cwmp:CancelTransfer/>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeWindowStruct struct {
|
||||||
|
WindowStart string
|
||||||
|
WindowEnd string
|
||||||
|
WindowMode string
|
||||||
|
UserMessage string
|
||||||
|
MaxRetries string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (window *TimeWindowStruct) String() string {
|
||||||
|
return `<TimeWindowStruct>
|
||||||
|
<WindowStart>` + window.WindowStart + `</WindowStart>
|
||||||
|
<WindowEnd>` + window.WindowEnd + `</WindowEnd>
|
||||||
|
<WindowMode>` + window.WindowMode + `</WindowMode>
|
||||||
|
<UserMessage>` + window.UserMessage + `</UserMessage>
|
||||||
|
<MaxRetries>` + window.MaxRetries + `</MaxRetries>
|
||||||
|
</TimeWindowStruct>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ScheduleDownload(filetype, url, username, password, filesize string, windowslist []fmt.Stringer) string {
|
||||||
|
ret := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cwmp:ScheduleDownload>
|
||||||
|
<CommandKey>MSDWK</CommandKey>
|
||||||
|
<FileType>` + filetype + `</FileType>
|
||||||
|
<URL>` + url + `</URL>
|
||||||
|
<Username>` + username + `</Username>
|
||||||
|
<Password>` + password + `</Password>
|
||||||
|
<FileSize>` + filesize + `</FileSize>
|
||||||
|
<TargetFileName></TargetFileName>
|
||||||
|
<TimeWindowList>`
|
||||||
|
|
||||||
|
for _, op := range windowslist {
|
||||||
|
ret += op.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += `</TimeWindowList>
|
||||||
|
</cwmp:ScheduleDownload>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type InstallOpStruct struct {
|
||||||
|
Url string
|
||||||
|
Uuid string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
ExecutionEnvironment string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *InstallOpStruct) String() string {
|
||||||
|
return `<InstallOpStruct>
|
||||||
|
<URL>` + op.Url + `</URL>
|
||||||
|
<UUID>` + op.Uuid + `</UUID>
|
||||||
|
<Username>` + op.Username + `</Username>
|
||||||
|
<Password>` + op.Password + `</Password>
|
||||||
|
<ExecutionEnvRef>` + op.ExecutionEnvironment + `</ExecutionEnvRef>
|
||||||
|
</InstallOpStruct>`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateOpStruct struct {
|
||||||
|
Uuid string
|
||||||
|
Version string
|
||||||
|
Url string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *UpdateOpStruct) String() string {
|
||||||
|
return `<UpdateOpStruct>
|
||||||
|
<UUID>` + op.Uuid + `</UUID>
|
||||||
|
<Version>` + op.Version + `</Version>
|
||||||
|
<URL>` + op.Url + `</URL>
|
||||||
|
<Username>` + op.Username + `</Username>
|
||||||
|
<Password>` + op.Password + `</Password>
|
||||||
|
</UpdateOpStruct>`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UninstallOpStruct struct {
|
||||||
|
Uuid string
|
||||||
|
Version string
|
||||||
|
ExecutionEnvironment string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *UninstallOpStruct) String() string {
|
||||||
|
return `<UninstallOpStruct>
|
||||||
|
<UUID>` + op.Uuid + `</UUID>
|
||||||
|
<Version>` + op.Version + `</Version>
|
||||||
|
<ExecutionEnvRef>` + op.ExecutionEnvironment + `</ExecutionEnvRef>
|
||||||
|
</UninstallOpStruct>`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChangeDuState(ops []fmt.Stringer) string {
|
||||||
|
ret := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:schemaLocation="urn:dslforum-org:cwmp-1-0 ..\schemas\wt121.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<soap:Header/>
|
||||||
|
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<cmwp:ChangeDUState>
|
||||||
|
<Operations>`
|
||||||
|
|
||||||
|
for _, op := range ops {
|
||||||
|
ret += op.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += `</Operations>
|
||||||
|
<CommandKey></CommandKey>
|
||||||
|
</cmwp:ChangeDUState>
|
||||||
|
</soap:Body>
|
||||||
|
</soap:Envelope>`
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPE side
|
||||||
|
|
||||||
|
func Inform(serial string) string {
|
||||||
|
return `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0"><soap:Header><cwmp:ID soap:mustUnderstand="1">5058</cwmp:ID></soap:Header>
|
||||||
|
<soap:Body><cwmp:Inform><DeviceId><Manufacturer>ADB Broadband</Manufacturer>
|
||||||
|
<OUI>0013C8</OUI>
|
||||||
|
<ProductClass>VV5522</ProductClass>
|
||||||
|
<SerialNumber>` + serial + `</SerialNumber>
|
||||||
|
</DeviceId>
|
||||||
|
<Event soap-enc:arrayType="cwmp:EventStruct[1]">
|
||||||
|
<EventStruct><EventCode>6 CONNECTION REQUEST</EventCode>
|
||||||
|
<CommandKey></CommandKey>
|
||||||
|
</EventStruct>
|
||||||
|
</Event>
|
||||||
|
<MaxEnvelopes>1</MaxEnvelopes>
|
||||||
|
<CurrentTime>` + time.Now().Format(time.RFC3339) + `</CurrentTime>
|
||||||
|
<RetryCount>0</RetryCount>
|
||||||
|
<ParameterList soap-enc:arrayType="cwmp:ParameterValueStruct[8]">
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.ManagementServer.ConnectionRequestURL</Name>
|
||||||
|
<Value xsi:type="xsd:string">http://104.199.175.27:7547/ConnectionRequest-` + serial + `</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.ManagementServer.ParameterKey</Name>
|
||||||
|
<Value xsi:type="xsd:string"></Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.DeviceSummary</Name>
|
||||||
|
<Value xsi:type="xsd:string">InternetGatewayDevice:1.2[](Baseline:1,EthernetLAN:1,WiFiLAN:1,ADSLWAN:1,EthernetWAN:1,QoS:1,QoSDynamicFlow:1,Bridging:1,Time:1,IPPing:1,TraceRoute:1,DeviceAssociation:1,UDPConnReq:1),VoiceService:1.0[1](TAEndpoint:1,SIPEndpoint:1)</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.DeviceInfo.HardwareVersion</Name>
|
||||||
|
<Value xsi:type="xsd:string">` + serial + `</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.DeviceInfo.ProvisioningCode</Name>
|
||||||
|
<Value xsi:type="xsd:string">ABCD</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.DeviceInfo.SoftwareVersion</Name>
|
||||||
|
<Value xsi:type="xsd:string">4.0.8.17785</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.DeviceInfo.SpecVersion</Name>
|
||||||
|
<Value xsi:type="xsd:string">1.0</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
<ParameterValueStruct><Name>InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress</Name>
|
||||||
|
<Value xsi:type="xsd:string">12.0.0.10</Value>
|
||||||
|
</ParameterValueStruct>
|
||||||
|
</ParameterList>
|
||||||
|
</cwmp:Inform>
|
||||||
|
</soap:Body></soap:Envelope>`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func BuildGetParameterValuesResponse(serial string, leaves GetParameterValues_) string {
|
||||||
|
ret := `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0">
|
||||||
|
<soap:Header><cwmp:ID soap:mustUnderstand="1">3</cwmp:ID></soap:Header>
|
||||||
|
<soap:Body><cwmp:GetParameterValuesResponse>`
|
||||||
|
|
||||||
|
db, _ := sqlite3.Open("/tmp/cpe.db")
|
||||||
|
|
||||||
|
n_leaves := 0
|
||||||
|
var temp string
|
||||||
|
for _, leaf := range leaves.ParameterNames {
|
||||||
|
sql := "select key, value, tipo from params where key like '" + leaf + "%'"
|
||||||
|
for s, err := db.Query(sql); err == nil; err = s.Next() {
|
||||||
|
n_leaves++
|
||||||
|
var key string
|
||||||
|
var value string
|
||||||
|
var tipo string
|
||||||
|
s.Scan(&key, &value, &tipo)
|
||||||
|
temp += `<ParameterValueStruct>
|
||||||
|
<Name>` + key + `</Name>
|
||||||
|
<Value xsi:type="` + tipo + `">` + value + `</Value>
|
||||||
|
</ParameterValueStruct>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += `<ParameterList soap-enc:arrayType="cwmp:ParameterValueStruct[` + strconv.Itoa(n_leaves) + `]">`
|
||||||
|
ret += temp
|
||||||
|
ret += `</ParameterList></cwmp:GetParameterValuesResponse></soap:Body></soap:Envelope>`
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildGetParameterNamesResponse(serial string, leaves GetParameterNames_) string {
|
||||||
|
ret := `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0">
|
||||||
|
<soap:Header><cwmp:ID soap:mustUnderstand="1">69</cwmp:ID></soap:Header>
|
||||||
|
<soap:Body><cwmp:GetParameterNamesResponse>`
|
||||||
|
db, _ := sqlite3.Open("/tmp/cpe.db")
|
||||||
|
|
||||||
|
obj := make(map[string]bool)
|
||||||
|
var temp string
|
||||||
|
for _, leaf := range leaves.ParameterPath {
|
||||||
|
fmt.Println(leaf)
|
||||||
|
sql := "select key, value, tipo from params where key like '" + leaf + "%'"
|
||||||
|
for s, err := db.Query(sql); err == nil; err = s.Next() {
|
||||||
|
var key string
|
||||||
|
var value string
|
||||||
|
var tipo string
|
||||||
|
s.Scan(&key, &value, &tipo)
|
||||||
|
var sp = strings.Split(strings.Split(key, leaf)[1], ".")
|
||||||
|
nextlevel, _ := strconv.Atoi(leaves.NextLevel)
|
||||||
|
if nextlevel == 0 {
|
||||||
|
root := leaf
|
||||||
|
obj[root] = true
|
||||||
|
for idx := range sp {
|
||||||
|
if idx == len(sp)-1 {
|
||||||
|
root = root + sp[idx]
|
||||||
|
} else {
|
||||||
|
root = root + sp[idx] + "."
|
||||||
|
}
|
||||||
|
obj[root] = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !obj[sp[0]] {
|
||||||
|
if len(sp) > 1 {
|
||||||
|
obj[leaf+sp[0]+"."] = true
|
||||||
|
} else {
|
||||||
|
obj[leaf+sp[0]] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for o := range obj {
|
||||||
|
temp += `<ParameterInfoStruct>
|
||||||
|
<Name>` + o + `</Name>
|
||||||
|
<Writable>true</Writable>
|
||||||
|
</ParameterInfoStruct>`
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(len(obj))
|
||||||
|
ret += `<ParameterList soap-enc:arrayType="cwmp:ParameterInfoStruct[]">`
|
||||||
|
ret += temp
|
||||||
|
ret += `</ParameterList></cwmp:GetParameterNamesResponse></soap:Body></soap:Envelope>`
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
@ -3,7 +3,7 @@ package entity
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type DataType interface {
|
type DataType interface {
|
||||||
[]map[string]interface{} | *string | Device | int64 | []Device | []VendorsCount | []ProductClassCount | []StatusCount | time.Duration
|
[]map[string]interface{} | *string | Device | int64 | []Device | []VendorsCount | []ProductClassCount | []StatusCount | time.Duration | []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type MsgAnswer[T DataType] struct {
|
type MsgAnswer[T DataType] struct {
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ const (
|
||||||
NATS_WS_ADAPTER_SUBJECT_PREFIX = "ws-adapter.usp.v1."
|
NATS_WS_ADAPTER_SUBJECT_PREFIX = "ws-adapter.usp.v1."
|
||||||
NATS_STOMP_ADAPTER_SUBJECT_PREFIX = "stomp-adapter.usp.v1."
|
NATS_STOMP_ADAPTER_SUBJECT_PREFIX = "stomp-adapter.usp.v1."
|
||||||
DEVICE_SUBJECT_PREFIX = "device.usp.v1."
|
DEVICE_SUBJECT_PREFIX = "device.usp.v1."
|
||||||
|
DEVICE_CWMP_SUBJECT_PREFIX = "device.cwmp.v1."
|
||||||
BUCKET_NAME = "devices-auth"
|
BUCKET_NAME = "devices-auth"
|
||||||
BUCKET_DESCRIPTION = "Devices authentication"
|
BUCKET_DESCRIPTION = "Devices authentication"
|
||||||
|
NATS_CWMP_ADAPTER_SUBJECT_PREFIX = "cwmp-adapter.v1."
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartNatsClient(c config.Nats) (jetstream.JetStream, *nats.Conn, jetstream.KeyValue) {
|
func StartNatsClient(c config.Nats) (jetstream.JetStream, *nats.Conn, jetstream.KeyValue) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user