sysclient

package
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2023 License: MIT Imports: 19 Imported by: 2

README

Sysclient

An implementation of the Sysclient protocol described at Sysclient protocol

  • connects a Go client to the innovaphone Devices App as a sysclient like any other device.
  • can be used to provide a web interface with text or other functions.
  • or to record or check the configurations devices receive from the Devices app.
  • useful during the development of myApps apps when they have to have access to devices. Allows to use mock/dummy devices instead of having to maintain real ones.

Example usage of the libary

This example code deploys a sysclient that connects to the Devices app's websocket. It then serves HTTP endpoints that are accessible through the interface in the Devices app. It issues the requests with the received configurations on the console.

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/ricoschulte/go-myapps/sysclient"
)



func main() {
	// define the properties of the device that connects to the devices app as sysclient
	identity := sysclient.Identity{
		Id:      "f19033480af9",
		Product: "IP232",
		Version: "13r2 dvl [13.4250/131286/1300]",
		FwBuild: "134250",
		BcBuild: "131286",
		Major:   "13r2",
		Fw:      "ip222.bin",
		Bc:      "boot222.bin",
		Mini:    false,
		PbxActive: false,
		Other: false,
		Platform: sysclient.Platform{
			Type: "PHONE",
		},
		EthIfs: []sysclient.EthIf{
			{
				If:   "ETH0",
				Ipv4: "172.16.4.141",
				Ipv6: "2002:91fd:9d07:0:290:33ff:fe46:af2",
			},
		},
	}

	sc, err_creating_sysclient := sysclient.NewSysclient(
		// the identity from above
		identity,

		
		// the Devices App URL to connect to
        // ws[s]://<ip/host>[:<port>]/<domain></instance>/sysclients
		"wss://apps.company.com/company.com/devices/sysclients",

		// a timeout duration for the websocket
		time.Duration(2*time.Second),

		// InsecureSkipVerify: if true, the verification of the TLS certificate is skipped.
		// Do not use in production. You have been warned.
		false,

		// a SeveMux for requested HTTP request, see below
		getServeMux(),

		// filenames to store the password of the sysclient and a received and decoded admin password
		"sysclient_password.txt",
		"sysclient_administrativepassword.txt",
	)

	// when no error has happend while creating the client ...
	if err_creating_sysclient != nil {
		panic(err_creating_sysclient)
	}
	// ... start it.
	sc.Connect()

	select {} // keep the program running
}

// create a ServeMux for handling Tunnel Http Requests
func getServeMux() *http.ServeMux {
	mux := http.NewServeMux()

	// Serve a file for the /admin.xml path
	mux.HandleFunc("/admin.xml", func(w http.ResponseWriter, r *http.Request) {
		response_text := `<html><body>`
		response_text += fmt.Sprintf(`page on %s`, r.URL.Path)
		response_text += "</body></html>"

		w.Header().Add("Content-Length", fmt.Sprint(len(response_text)))
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(response_text))
	})

	// Serve a file for the /CMD0/mod_cmd.xml endpoint the will receive configurations
	mux.HandleFunc("/CMD0/mod_cmd.xml", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("---------------------------")
		fmt.Println("received on", r.URL.Path)
		fmt.Println("received headers", r.Header)
		for key, value := range r.URL.Query() {
			fmt.Println("received param", key, value)
		}

		response_text := `<html><body>`
		response_text += fmt.Sprintf(`mod_cmd.xml page on %s`, r.URL.Path)
		response_text += "</body></html>"

		w.Header().Add("Content-Length", fmt.Sprint(len(response_text)))
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(response_text))
	})

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			// catch not existing path with error log
			response_text := fmt.Sprintf("Path %s not found", r.URL.Path)
			fmt.Println("REQUEST 404", r.URL.Path)
			w.Header().Add("Content-Length", fmt.Sprint(len(response_text)))
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(response_text))

			return
		}

		response_text := `<html><body>`
		response_text += fmt.Sprintf(`page on %s`, r.URL.Path)
		response_text += "</body></html>"

		w.Header().Add("Content-Length", fmt.Sprint(len(response_text)))
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(response_text))

	})

	return mux
}

Documentation

Index

Constants

View Source
const (
	MessageTypeAdmin byte = 0x01
)

Sysclient message header

Variables

View Source
var (
	MessageTypeTunnel byte = 0x02

	TunnelSend          []byte = []byte{0x00, 0x00, 0x00, 0x00} // socket send
	TunnelReceive       []byte = []byte{0x00, 0x00, 0x00, 0x01} // socket receive
	TunnelReceiveResult []byte = []byte{0x00, 0x00, 0x00, 0x02} // receive result
	TunnelSendResult    []byte = []byte{0x00, 0x00, 0x00, 0x03} // socket send result
	TunnelShutdown      []byte = []byte{0x00, 0x00, 0x00, 0x04} // socket shutdown
)

Sysclient message header

View Source
var AdminConfiguration []byte = []byte{0x00, 0x06} // Configuration
View Source
var AdminProvision []byte = []byte{0x00, 0x04} // Provision
View Source
var AdminProvisionResult []byte = []byte{0x00, 0x05} // Provision result
View Source
var AdminReceiveChallenge []byte = []byte{0x00, 0x02} // Receive challenge
View Source
var AdminReceiveNewAdminPassword []byte = []byte{0x00, 0x03} // Receive new administrative password
View Source
var AdminReceiveSysclientPassword []byte = []byte{0x00, 0x01} // Receive sysclient password
View Source
var AdminSendIdentify []byte = []byte{0x00, 0x00} // Send identify

Admin message header

Functions

This section is empty.

Types

type AdminMessage

type AdminMessage struct {
	Type    byte
	Command []byte // 2 bytes
	Data    []byte // the rest of the data

}

func NewAdminMessage

func NewAdminMessage(message []byte) (*AdminMessage, error)

func (*AdminMessage) AsBytes

func (am *AdminMessage) AsBytes() []byte

func (*AdminMessage) DecryptAdminPassword

func (am *AdminMessage) DecryptAdminPassword(sysclientPassword string, seed string, pwd string) ([]byte, error)

func (*AdminMessage) GetLoginDigest

func (am *AdminMessage) GetLoginDigest(id, product, version, challenge, password string) (string, error)

func (*AdminMessage) HandleAdminReceiveChallenge

func (am *AdminMessage) HandleAdminReceiveChallenge(secretKey []byte, deviceInfo *Identity, fileSysclientpassword string) (*AdminMessage, error)

func (*AdminMessage) HandleAdminReceiveSysclientPassword

func (am *AdminMessage) HandleAdminReceiveSysclientPassword(secretKey []byte, fileSysclientpassword string) error

receives the sysclient password and stores it. no answer is required

type AdministrativePassword

type AdministrativePassword struct {
	Seed string `json:"seed"`
	Pwd  string `json:"pwd"`
}

func NewAdministrativePassword

func NewAdministrativePassword(json_bytes []byte) (*AdministrativePassword, error)

type Challenge

type Challenge struct {
	Challenge string `json:"challenge"`
}

func NewChallenge

func NewChallenge(json_bytes []byte) (*Challenge, error)

type EthIf

type EthIf struct {
	If   string `json:"if"`
	Ipv4 string `json:"ipv4"`
	Ipv6 string `json:"ipv6"`
}

type HttpResponseWriter

type HttpResponseWriter struct {
	// contains filtered or unexported fields
}

Define a HttpResponseWriter ResponseWriter

func (*HttpResponseWriter) GetBytes

func (w *HttpResponseWriter) GetBytes() []byte

func (*HttpResponseWriter) Header

func (w *HttpResponseWriter) Header() http.Header

func (*HttpResponseWriter) Write

func (w *HttpResponseWriter) Write(p []byte) (int, error)

func (*HttpResponseWriter) WriteHeader

func (w *HttpResponseWriter) WriteHeader(status int)

type Identity

type Identity struct {
	Id        string   `json:"id"`
	Product   string   `json:"product"`
	Version   string   `json:"version"`
	FwBuild   string   `json:"fwBuild"`
	BcBuild   string   `json:"bcBuild"`
	Major     string   `json:"major"`
	Fw        string   `json:"fw"`
	Bc        string   `json:"bc"`
	Mini      bool     `json:"mini"`
	PbxActive bool     `json:"pbxActive"`
	Other     bool     `json:"other"`
	Platform  Platform `json:"platform"`
	Digest    string   `json:"digest,omitempty"`
	EthIfs    []EthIf  `json:"ethIfs,omitempty"` // up to 3 eth interfaces are allowed.
}

func NewIdentity

func NewIdentity(json_bytes []byte) (*Identity, error)

func (Identity) ToBytes

func (i Identity) ToBytes() ([]byte, error)

type Password

type Password struct {
	Password string `json:"password"`
}

func NewPassword

func NewPassword(json_bytes []byte) (*Password, error)

type Platform

type Platform struct {
	Type string `json:"type"`
	Fxs  bool   `json:"fxs"`
}

type Sysclient

type Sysclient struct {
	Identity           Identity
	Url                string
	Timeout            time.Duration
	InsecureSkipVerify bool
	Context            context.Context
	Conn               *websocket.Conn
	Tunnels            map[int32]*SysclientTunnel // map of active tunnels indexed by the sessionid
	ServeMux           *http.ServeMux             // the instance of the Http Server Mux to handle Http Requests

	FileSysclientPassword      string // filename to store
	FileAdministrativePassword string // filename to store
	SecretKey                  []byte // key to encrypt the local files as []bytes
}

func NewSysclient

func NewSysclient(identity Identity, url string, timeout time.Duration, insecureSkipVerify bool, mux *http.ServeMux, fileSysclientPassword string, fileAdministrativePassword string, secretkey string) (*Sysclient, error)

func (*Sysclient) Connect

func (sc *Sysclient) Connect() error

func (*Sysclient) HandleAdminMessage

func (sc *Sysclient) HandleAdminMessage(messageIn *AdminMessage) (*AdminMessage, error)

func (*Sysclient) Printf

func (sc *Sysclient) Printf(format string, a ...interface{})

func (*Sysclient) Println

func (sc *Sysclient) Println(v ...any)

func (*Sysclient) ReadWriteLoop

func (sc *Sysclient) ReadWriteLoop() error

func (*Sysclient) Send

func (sc *Sysclient) Send(message []byte) error

type SysclientTunnel

type SysclientTunnel struct {
	ServeMux       *http.ServeMux
	SessionId      []byte
	Response       []byte
	ResponseChunks [][]byte
}

func NewSysclientTunnel

func NewSysclientTunnel(session []byte, servermux *http.ServeMux) (*SysclientTunnel, error)

func (*SysclientTunnel) GetNextChunk

func (tunnel *SysclientTunnel) GetNextChunk() ([]byte, error)

func (*SysclientTunnel) HandleRequest

func (tunnel *SysclientTunnel) HandleRequest(message *TunnelMessage) (*TunnelMessage, error)

func (*SysclientTunnel) HandleTunnelReceive

func (tunnel *SysclientTunnel) HandleTunnelReceive(message *TunnelMessage) (*TunnelMessage, error)

in:

TunnelReceive
02 0000000d 00000001 00000000

out:

TunnelReceiveResult
02 0000000d 00000002 485454502f312e3120323030204f4b0d0a436f6e74656e742d547970653a206170706c69636174696f6e2f6a6176617363726970740d0a…

func (*SysclientTunnel) HandleTunnelSend

func (tunnel *SysclientTunnel) HandleTunnelSend(message *TunnelMessage) (*TunnelMessage, error)

the sysclient server is requesting data with http data

in:
	TunnelSend
	02 0000000d 00000000 474554202f7374617469632f6a732f706167652e6a7320485454502f312e310d0a486f73743a20617070732e667269747a2e626f780d0a…]
out:
	TunnelSendResult
	02 0000000d 00000003

type TunnelMessage

type TunnelMessage struct {
	Type      byte
	SessionId []byte // 4 bytes
	EventType []byte // 4 bytes
	Data      []byte // rest of the bytes
}

SMT | Byte offset

   |  0      1      2      3      4      5      6      7      8      9     ...
   +------+------+------+------+------+------+------+------+------+------+------+
2  |      |        Session-ID         |         Event-Type        |  Data.....
   +------+------+------+------+------+------+------+------+------+------+------+

func NewTunnelMessage

func NewTunnelMessage(message []byte) (*TunnelMessage, error)

func (*TunnelMessage) AsBytes

func (tm *TunnelMessage) AsBytes() []byte

func (*TunnelMessage) GetSessionId

func (tm *TunnelMessage) GetSessionId() (int32, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL