package ftp import ( "bufio" "crypto/tls" "errors" "fmt" "net" "strings" "time" "git.loafle.net/commons_go/logging" cnsm "git.loafle.net/commons_go/network_service_matcher" ) // FTP is a Session for file Transfer Protocol type FTPS struct { conn net.Conn addr string tlsconfig *tls.Config reader *bufio.Reader writer *bufio.Writer isFtps bool } func (fs *FTPS) close() { fs.conn.Close() } func (fs *FTPS) quit() (err error) { if _, err := fs.cmd(statusCloseConnect, "QUIT"); err != nil { return err } fs.conn.Close() fs.conn = nil return nil } func (fs *FTPS) cmd(expects string, cmd string) (line string, err error) { if err = fs.send(cmd); err != nil { logging.Logger().Error(fmt.Sprintf("Discovery: FTPS Matcher cmd send error %v", err)) return "", err } if line, err = fs.receive(); err != nil { logging.Logger().Error(fmt.Sprintf("Discovery: FTPS Matcher cmd receive error %v", err)) return line, err } if !strings.HasPrefix(line, expects) { err = errors.New(line) return line, err } return line, err } func (fs *FTPS) readAndDiscard() (int, error) { var i int bufferSize := fs.reader.Buffered() for i = 0; i < bufferSize; i++ { if _, err := fs.reader.ReadByte(); err != nil { return i, err } } return i, nil } func (fs *FTPS) receive() (string, error) { line, err := fs.reader.ReadString('\n') //log.Debug("< %s", line) if err != nil { return line, err } fs.readAndDiscard() //fmt.Println(line) return line, err } func (fs *FTPS) send(cmd string) (err error) { if len(cmd) == 0 { err = errors.New("command length 0") } cmd = fmt.Sprintf(cmd) cmd += "\r\n" if _, err := fs.writer.WriteString(cmd); err != nil { return err } if err := fs.writer.Flush(); err != nil { return err } return nil } func (fs *FTPS) authTls(config *tls.Config) error { if _, err := fs.cmd(statusTlsUseOK, "AUTH TLS"); err != nil { return err } fs.tlsconfig = config fs.conn = tls.Client(fs.conn, config) fs.writer = bufio.NewWriter(fs.conn) fs.reader = bufio.NewReader(fs.conn) _, err := fs.cmd(statusOK, "PBSZ 0") if err != nil { return err } _, err = fs.cmd(statusOK, "PROT P") if err != nil { return err } return nil } func (fs *FTPS) NewFTPSConnect(addr string) (*FTPS, error) { var err error var conn net.Conn if conn, err = net.Dial("tcp", addr); err != nil { logging.Logger().Error(fmt.Sprintf("Discovery: FTPS Matcher Socket Fail %v", err)) return nil, err } err = conn.SetDeadline(time.Now().Add(3 * time.Second)) if err != nil { //log.Error("FTPS Socket Fail: ", err.Error()) return nil, err } writer := bufio.NewWriter(conn) reader := bufio.NewReader(conn) var line string obj := &FTPS{ conn: conn, addr: addr, reader: reader, writer: writer, } line, err = obj.receive() if !strings.HasPrefix(line, "220") { err = errors.New(line) return nil, err } //log.Debug(line) return obj, err } func StartCheckFTPS(info cnsm.MatchInfo) (bool, error) { var err error var fs *FTPS addr := fmt.Sprintf("%s:%d", info.IP(), info.Port()) //log.Debug("address : " + addr) if fs, err = fs.NewFTPSConnect(addr); err != nil { return false, err } defer fs.close() config := &tls.Config{ InsecureSkipVerify: true, ClientAuth: tls.RequestClientCert, } if err = fs.authTls(config); err != nil { return false, err } return true, err }