-
Notifications
You must be signed in to change notification settings - Fork 1
/
ftp.go
104 lines (95 loc) · 2.8 KB
/
ftp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package services
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"strings"
"github.com/ScoreTrak/ScoreTrak/pkg/exec"
"github.com/jlaffaye/ftp"
)
type FTP struct {
Username string
Password string
Port string
// Text to upload to remote computer
Text string
// Name of the file to which write the parameter
WriteFilename string
// Filename to find on a remote computer
ReadFilename string
// Check expected output of the file
ExpectedOutput string
}
func NewFTP() *FTP {
f := FTP{Port: "21"}
return &f
}
var ErrFTPNeedsUsernameAndPassword = errors.New("FTP check_service needs username and password")
var ErrFTPNeedsEitherTextOrReadFile = errors.New("FTP check_service needs either text, or read_file parameter")
func (f *FTP) Validate() error {
if f.Password != "" && f.Username != "" {
if f.Text != "" || f.ReadFilename != "" {
return nil
}
return ErrFTPNeedsEitherTextOrReadFile
}
return ErrFTPNeedsUsernameAndPassword
}
func (f *FTP) Execute(e exec.Exec) (passed bool, l string, err error) {
dialOptions := ftp.DialWithContext(e.Context)
c, err := ftp.Dial(fmt.Sprintf("%s:%s", e.Host, f.Port), dialOptions) // For passive FTP allow Data Channel Port Range. In addition, Allow FTP as an APP in windows firewall, and allow port 20, 21, 1024-65535
if err != nil {
return false, "", fmt.Errorf("unable to dial FTP Server: %w", err)
}
defer func(c *ftp.ServerConn) {
err := c.Quit()
if err != nil {
log.Println(fmt.Errorf("unable to close ftp connection: %w", err))
}
}(c)
err = c.Login(f.Username, f.Password)
if err != nil {
return false, "", fmt.Errorf("unable to Login: %w", err)
}
defer func(c *ftp.ServerConn) {
err := c.Logout()
if err != nil {
log.Println(fmt.Errorf("unable to logout from FTP: %w", err))
}
}(c)
if f.Text != "" {
data := bytes.NewBufferString(f.Text)
if f.WriteFilename == "" {
f.WriteFilename = "test_file.txt"
}
err = c.Stor(f.WriteFilename, data)
if err != nil {
return false, "", fmt.Errorf("unable to Store file to FTP server: %w", err)
}
}
if f.ReadFilename != "" {
r, err := c.Retr(f.ReadFilename)
if err != nil {
return false, "", fmt.Errorf("failed to retrieve the file from FTP: %w", err)
}
defer func(r *ftp.Response) {
err := r.Close()
if err != nil {
log.Println(fmt.Errorf("unable to close file: %w", err))
}
}(r)
if err := c.Quit(); err != nil {
return false, "", fmt.Errorf("unable to gracefully exit FTP server: %w", err)
}
buf, err := ioutil.ReadAll(r)
if err != nil {
return false, "", fmt.Errorf("failed to read file contents, it might be corrupted: %w", err)
}
if f.ExpectedOutput != "" && !strings.Contains(string(buf), f.ExpectedOutput) {
return false, "", fmt.Errorf("%w. Output Received: %s", ErrDidNotMatchExpectedOutput, string(buf))
}
}
return true, Success, nil
}