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 }