|
|
@@ -0,0 +1,243 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "os"
|
|
|
+ "time"
|
|
|
+ "encoding/json"
|
|
|
+ "encoding/binary"
|
|
|
+ "net"
|
|
|
+ "fmt"
|
|
|
+ "errors"
|
|
|
+)
|
|
|
+
|
|
|
+func (o *Oftp) New(filename string) error {
|
|
|
+ // Set default values
|
|
|
+ newOftp := Oftp{
|
|
|
+ LocalCode: "LOCALCODE",
|
|
|
+ LocalPassword: "LOCALPWD",
|
|
|
+ PartnerCode: "1234567890CODE",
|
|
|
+ PartnerPassword: "SUPERSECRET",
|
|
|
+ OftpLevel: 4,
|
|
|
+ OftpBuffer: 512,
|
|
|
+ OftpDuplex: "S",
|
|
|
+ OftpCompression: "N",
|
|
|
+ OftpRestart: "N",
|
|
|
+ OftpCredit: 7,
|
|
|
+ OftpAuthentication: "N",
|
|
|
+ NetworkHost: "localhost",
|
|
|
+ NetworkPort: 3305,
|
|
|
+ NetworkTLS: false,
|
|
|
+ }
|
|
|
+
|
|
|
+ f, err := os.Open(filename)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer f.Close()
|
|
|
+
|
|
|
+ d := json.NewDecoder(f)
|
|
|
+
|
|
|
+ if err := d.Decode(&newOftp); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ *o = newOftp
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (o Oftp) Call() error {
|
|
|
+ // 1. Open connection
|
|
|
+ // 2. Wait for SSRM
|
|
|
+ // 3. Send SSID
|
|
|
+ // 4. Wait for SSID
|
|
|
+ // 5. Validate SSID
|
|
|
+ // 6. Send ESID
|
|
|
+
|
|
|
+ // 1. Open connection
|
|
|
+ addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", o.NetworkHost, o.NetworkPort))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ conn, err := net.DialTCP("tcp", nil, addr)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer conn.Close()
|
|
|
+
|
|
|
+ fmt.Printf("Connected to %s\n", addr.String())
|
|
|
+
|
|
|
+ // 2. Wait for SSRM
|
|
|
+ headerBuf := make([]byte, 4)
|
|
|
+ dataBuf := make([]byte, o.OftpBuffer)
|
|
|
+
|
|
|
+ // Read STH (header)
|
|
|
+ readCnt, err := conn.Read(headerBuf)
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ length, err := parseSTH(headerBuf[:4])
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("STH says STB is %d long\n", length)
|
|
|
+
|
|
|
+ // Read OFTP command
|
|
|
+ readCnt, err = conn.Read(dataBuf)
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("Read %d, should be %d\n", readCnt, length - 4)
|
|
|
+
|
|
|
+ if string(dataBuf[:readCnt]) != "IODETTE FTP READY \r" && string(dataBuf[:readCnt]) != "IODETTE FTP READY \n"{
|
|
|
+ conn.Close()
|
|
|
+ return errors.New(fmt.Sprintf("Expected SSRM (%x), got %x", "IODETTE FTP READY \r", dataBuf[:readCnt]))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. Send SSID
|
|
|
+ fmt.Println(string(o.LocalSSID()))
|
|
|
+ writtenCnt, err := conn.Write(o.LocalSSID())
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("Write %d bytes to %s\n", writtenCnt, addr.String())
|
|
|
+
|
|
|
+ // 4. Wait for SSID
|
|
|
+ // Read STH (header)
|
|
|
+ readCnt, err = conn.Read(headerBuf)
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ length, err = parseSTH(headerBuf[:4])
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("STH says STB is %d long\n", length)
|
|
|
+
|
|
|
+ // Read OFTP command
|
|
|
+ readCnt, err = conn.Read(dataBuf)
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("Read %d, should be %d\n", readCnt, length - 4)
|
|
|
+
|
|
|
+ // 5. Validate SSID
|
|
|
+ ESIDCode := o.ValidateSSID(dataBuf[:readCnt]);
|
|
|
+ if ESIDCode < 0 {
|
|
|
+ // If ESID is received, disconnect
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. Send ESID
|
|
|
+ fmt.Println(string(o.ESID(ESIDCode)))
|
|
|
+ writtenCnt, err = conn.Write(o.ESID(ESIDCode))
|
|
|
+ if err != nil {
|
|
|
+ conn.Close()
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("Write %d bytes to %s\n", writtenCnt, addr.String())
|
|
|
+
|
|
|
+ // Wait for partner to disconnect
|
|
|
+ conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
|
|
+ conn.Read(dataBuf)
|
|
|
+ conn.Close()
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (o Oftp) LocalSSID() []byte {
|
|
|
+ dataBuf := [61]byte{}
|
|
|
+ copy(dataBuf[0:1], "X")
|
|
|
+ copy(dataBuf[1:2], fmt.Sprintf("%d", o.OftpLevel))
|
|
|
+ copy(dataBuf[2:27], o.LocalCode)
|
|
|
+ copy(dataBuf[27:35], o.LocalPassword)
|
|
|
+ copy(dataBuf[35:40], fmt.Sprintf("%05d", o.OftpBuffer))
|
|
|
+ copy(dataBuf[40:41], o.OftpDuplex)
|
|
|
+ copy(dataBuf[41:42], o.OftpCompression)
|
|
|
+ copy(dataBuf[42:43], o.OftpRestart)
|
|
|
+ copy(dataBuf[43:44], "N")
|
|
|
+ copy(dataBuf[44:47], fmt.Sprintf("%03d", o.OftpCredit))
|
|
|
+ copy(dataBuf[47:48], o.OftpAuthentication)
|
|
|
+ copy(dataBuf[60:61], "\r")
|
|
|
+
|
|
|
+ sth := buildSTH(len(dataBuf))
|
|
|
+
|
|
|
+ stb := []byte{}
|
|
|
+
|
|
|
+ stb = append(stb, sth...)
|
|
|
+ stb = append(stb, dataBuf[:]...)
|
|
|
+
|
|
|
+ return stb
|
|
|
+}
|
|
|
+
|
|
|
+func (o Oftp) ESID(code int) []byte {
|
|
|
+ dataBuf := [7]byte{}
|
|
|
+ copy(dataBuf[0:1], "F")
|
|
|
+ copy(dataBuf[1:3], fmt.Sprintf("%02d", code))
|
|
|
+ copy(dataBuf[3:6], fmt.Sprintf("%03d", 0))
|
|
|
+ copy(dataBuf[6:7], "\r")
|
|
|
+
|
|
|
+ sth := buildSTH(len(dataBuf))
|
|
|
+ stb := []byte{}
|
|
|
+
|
|
|
+ stb = append(stb, sth...)
|
|
|
+ stb = append(stb, dataBuf[:]...)
|
|
|
+
|
|
|
+ return stb
|
|
|
+}
|
|
|
+
|
|
|
+func (o Oftp) ValidateSSID(SSIDBytes []byte) int {
|
|
|
+ // Validate cmd, code and password only
|
|
|
+ fmt.Println(string(SSIDBytes))
|
|
|
+ cmdByte := string(SSIDBytes[0:1])
|
|
|
+ if cmdByte != "X" {
|
|
|
+ return -1
|
|
|
+ }
|
|
|
+
|
|
|
+ codeBytes := string(SSIDBytes[2:27])
|
|
|
+ if codeBytes != fmt.Sprintf("%-25s", o.PartnerCode) {
|
|
|
+ return 3
|
|
|
+ }
|
|
|
+
|
|
|
+ passwordBytes := string(SSIDBytes[27:35])
|
|
|
+ if passwordBytes != fmt.Sprintf("%-8s", o.PartnerPassword) {
|
|
|
+ return 4
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func parseSTH(sth []byte) (int32, error) {
|
|
|
+ sth[0] = 0
|
|
|
+ length := binary.BigEndian.Uint32(sth)
|
|
|
+ return int32(length), nil
|
|
|
+}
|
|
|
+
|
|
|
+func buildSTH(length int) []byte {
|
|
|
+ sth := make([]byte, 4)
|
|
|
+ sth[0] = 16
|
|
|
+
|
|
|
+ lengthBytes := make([]byte, 4)
|
|
|
+ binary.BigEndian.PutUint32(lengthBytes, uint32(length + 4))
|
|
|
+ copy(sth[1:4], lengthBytes[1:4])
|
|
|
+
|
|
|
+ return sth
|
|
|
+
|
|
|
+}
|