feat: initial api for cwmp
This commit is contained in:
parent
5487ede44e
commit
406d62550f
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"oktopUSP/backend/services/acs/internal/bridge"
|
||||
"oktopUSP/backend/services/acs/internal/config"
|
||||
"oktopUSP/backend/services/acs/internal/nats"
|
||||
"oktopUSP/backend/services/acs/internal/server"
|
||||
|
|
@ -15,5 +16,12 @@ func main() {
|
|||
|
||||
h := handler.NewHandler(natsActions.Publish, natsActions.Subscribe)
|
||||
|
||||
b := bridge.NewBridge(
|
||||
natsActions.Publish,
|
||||
natsActions.Subscribe,
|
||||
h,
|
||||
)
|
||||
b.StartBridge()
|
||||
|
||||
server.Run(c.Acs, natsActions, h)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ module oktopUSP/backend/services/acs
|
|||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/nats-io/nats.go v1.34.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/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -36,37 +35,37 @@ func Auth(username string, password string, uri string) (bool, error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if resp.StatusCode == 401 {
|
||||
var authorization map[string]string = DigestAuthParams(resp)
|
||||
realmHeader := authorization["realm"]
|
||||
qopHeader := authorization["qop"]
|
||||
nonceHeader := authorization["nonce"]
|
||||
opaqueHeader := authorization["opaque"]
|
||||
realm := realmHeader
|
||||
// A1
|
||||
h := md5.New()
|
||||
A1 := fmt.Sprintf("%s:%s:%s", username, realm, password)
|
||||
io.WriteString(h, A1)
|
||||
HA1 := fmt.Sprintf("%x", h.Sum(nil))
|
||||
// if resp.StatusCode == 401 {
|
||||
// var authorization map[string]string = DigestAuthParams(resp)
|
||||
// realmHeader := authorization["realm"]
|
||||
// qopHeader := authorization["qop"]
|
||||
// nonceHeader := authorization["nonce"]
|
||||
// opaqueHeader := authorization["opaque"]
|
||||
// realm := realmHeader
|
||||
// // A1
|
||||
// h := md5.New()
|
||||
// A1 := fmt.Sprintf("%s:%s:%s", username, realm, password)
|
||||
// io.WriteString(h, A1)
|
||||
// HA1 := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
// A2
|
||||
h = md5.New()
|
||||
A2 := fmt.Sprintf("GET:%s", "/auth")
|
||||
io.WriteString(h, A2)
|
||||
HA2 := fmt.Sprintf("%x", h.Sum(nil))
|
||||
// // A2
|
||||
// h = md5.New()
|
||||
// A2 := fmt.Sprintf("GET:%s", "/auth")
|
||||
// io.WriteString(h, A2)
|
||||
// HA2 := fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
// response
|
||||
cnonce := RandomKey()
|
||||
response := H(strings.Join([]string{HA1, nonceHeader, "00000001", cnonce, qopHeader, HA2}, ":"))
|
||||
// // response
|
||||
// cnonce := RandomKey()
|
||||
// response := H(strings.Join([]string{HA1, nonceHeader, "00000001", cnonce, qopHeader, HA2}, ":"))
|
||||
|
||||
// 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`,
|
||||
username, realmHeader, nonceHeader, "/auth", cnonce, qopHeader, response, opaqueHeader)
|
||||
req.Header.Set("Authorization", AuthHeader)
|
||||
resp, err = client.Do(req)
|
||||
} else {
|
||||
return false, fmt.Errorf("response status code should have been 401, it was %v", resp.StatusCode)
|
||||
}
|
||||
// // 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`,
|
||||
// username, realmHeader, nonceHeader, "/auth", cnonce, qopHeader, response, opaqueHeader)
|
||||
// req.Header.Set("Authorization", AuthHeader)
|
||||
// resp, err = client.Do(req)
|
||||
// } else {
|
||||
// return false, fmt.Errorf("response status code should have been 401, it was %v", resp.StatusCode)
|
||||
// }
|
||||
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 (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"oktopUSP/backend/services/acs/internal/cwmp"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/oleiade/lane"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
const Version = "1.0.0"
|
||||
|
||||
type Request struct {
|
||||
Id string
|
||||
Websocket *websocket.Conn
|
||||
CwmpMessage string
|
||||
Callback func(msg *WsSendMessage) error
|
||||
User string
|
||||
Password string
|
||||
CwmpMsg []byte
|
||||
Callback chan []byte
|
||||
}
|
||||
|
||||
type CPE struct {
|
||||
|
|
@ -36,7 +30,8 @@ type CPE struct {
|
|||
HardwareVersion string
|
||||
LastConnection time.Time
|
||||
DataModel string
|
||||
KeepConnectionOpen bool
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
|
|
@ -48,7 +43,7 @@ type WsMessage struct {
|
|||
Cmd string
|
||||
}
|
||||
|
||||
type WsSendMessage struct {
|
||||
type NatsSendMessage struct {
|
||||
MsgType string
|
||||
Data json.RawMessage
|
||||
}
|
||||
|
|
@ -60,153 +55,19 @@ type MsgCPEs struct {
|
|||
type Handler struct {
|
||||
pub func(string, []byte) 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 {
|
||||
return &Handler{
|
||||
pub: pub,
|
||||
sub: sub,
|
||||
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)
|
||||
}
|
||||
}
|
||||
Cpes: make(map[string]CPE),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ import (
|
|||
|
||||
// TODO: make these consts dynamic via config
|
||||
const (
|
||||
CHECK_STATUS_INTERVAL = 5 * time.Second
|
||||
KEEP_ALIVE_INTERVAL = 10 * time.Second
|
||||
CHECK_STATUS_INTERVAL = 10 * time.Second
|
||||
KEEP_ALIVE_INTERVAL = 600 * time.Second
|
||||
)
|
||||
|
||||
func (h *Handler) handleCpeStatus(cpe string) {
|
||||
for {
|
||||
if time.Since(h.cpes[cpe].LastConnection) > KEEP_ALIVE_INTERVAL {
|
||||
delete(h.cpes, cpe)
|
||||
if time.Since(h.Cpes[cpe].LastConnection) > KEEP_ALIVE_INTERVAL {
|
||||
delete(h.Cpes, cpe)
|
||||
break
|
||||
}
|
||||
time.Sleep(CHECK_STATUS_INTERVAL)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
func Run(c config.Acs, natsActions nats.NatsActions, h *handler.Handler) {
|
||||
|
||||
http.HandleFunc(c.Route, h.CwmpHandler)
|
||||
|
||||
log.Printf("ACS running at %s%s", c.Port, c.Route)
|
||||
|
||||
err := http.ListenAndServe(c.Port, nil)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ func (a *Api) StartApi() {
|
|||
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
|
||||
iot := r.PathPrefix("/api/device").Subrouter()
|
||||
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("/{id}", a.retrieveDevices).Methods("GET")
|
||||
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
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ const (
|
|||
NATS_WS_ADAPTER_SUBJECT_PREFIX = "ws-adapter.usp.v1."
|
||||
NATS_STOMP_ADAPTER_SUBJECT_PREFIX = "stomp-adapter.usp.v1."
|
||||
DEVICE_SUBJECT_PREFIX = "device.usp.v1."
|
||||
DEVICE_CWMP_SUBJECT_PREFIX = "device.cwmp.v1."
|
||||
BUCKET_NAME = "devices-auth"
|
||||
BUCKET_DESCRIPTION = "Devices authentication"
|
||||
NATS_CWMP_ADAPTER_SUBJECT_PREFIX = "cwmp-adapter.v1."
|
||||
)
|
||||
|
||||
func StartNatsClient(c config.Nats) (jetstream.JetStream, *nats.Conn, jetstream.KeyValue) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user