Skip to content

Instantly share code, notes, and snippets.

@YOwatari
Created July 4, 2023 04:01
Show Gist options
  • Save YOwatari/f3373b385905796239e16aea0fd4b6a6 to your computer and use it in GitHub Desktop.
Save YOwatari/f3373b385905796239e16aea0fd4b6a6 to your computer and use it in GitHub Desktop.
Go Uses FTPS (require_ssl_reuse=YES)
package main
import (
"crypto/tls"
"fmt"
"io"
"log"
"net"
"net/textproto"
"strconv"
"strings"
)
const (
host = "127.0.0.1"
port = 21000
user = "user"
pass = "password"
)
// reuse session
type cache struct {
session *tls.ClientSessionState
}
func (c *cache) Get(_ string) (*tls.ClientSessionState, bool) {
return c.session, c.session != nil
}
func (c *cache) Put(_ string, cs *tls.ClientSessionState) {
c.session = cs
}
var (
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
ClientSessionCache: &cache{},
}
)
func main() {
if err := run(); err != nil {
panic(err)
}
}
func run() error {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return err
}
defer conn.Close()
tp := textproto.NewConn(conn)
if _, err := response(tp, 220); err != nil {
return err
}
if _, err := request(tp, "AUTH TLS", 234); err != nil {
return err
}
tlsConn := tls.Client(conn, tlsConfig)
tlsTp := textproto.NewConn(tlsConn)
if _, err := request(tlsTp, fmt.Sprintf("USER %s", user), 331); err != nil {
return err
}
if _, err := request(tlsTp, fmt.Sprintf("PASS %s", pass), 230); err != nil {
return err
}
if _, err := request(tlsTp, "FEAT", 211); err != nil {
return err
}
if _, err := request(tlsTp, "TYPE I", 200); err != nil {
return err
}
if _, err := request(tlsTp, "OPTS UTF8 ON", 200); err != nil {
return err
}
if _, err := request(tlsTp, "PBSZ 0", 200); err != nil {
return err
}
if _, err := request(tlsTp, "PROT P", 200); err != nil {
return err
}
b, err := responseData(tlsTp, "LIST", 150)
if err != nil {
return err
}
log.Print(string(b))
if _, err := request(tlsTp, "QUIT", 221); err != nil {
return err
}
return nil
}
func request(tp *textproto.Conn, cmd string, expectCode int) (string, error) {
log.Printf("%s", cmd)
id, err := tp.Cmd(cmd)
if err != nil {
return "", err
}
tp.StartResponse(id)
defer tp.EndResponse(id)
return response(tp, expectCode)
}
func response(tp *textproto.Conn, expectCode int) (string, error) {
code, message, err := tp.ReadResponse(expectCode)
if err != nil {
return "", err
}
log.Printf("%d %s", code, message)
return message, nil
}
func requestData(tp *textproto.Conn, cmd string, expectCode int) ([]byte, error) {
p, err := dataPort(tp)
if err != nil {
return nil, err
}
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, p))
if err != nil {
return nil, err
}
defer conn.Close()
tlsConn := tls.Client(conn, tlsConfig)
if _, err := request(tp, cmd, expectCode); err != nil {
return nil, err
}
b, err := io.ReadAll(tlsConn)
if err != nil {
return nil, err
}
return b, nil
}
func responseData(tp *textproto.Conn, cmd string, expectCode int) ([]byte, error) {
b, err := requestData(tp, cmd, expectCode)
if err != nil {
return nil, err
}
if _, err := response(tp, 226); err != nil {
return nil, err
}
return b, nil
}
func dataPort(tp *textproto.Conn) (int, error) {
msg, err := request(tp, "EPSV", 229)
if err != nil {
return 0, err
}
start := strings.Index(msg, "(")
end := strings.Index(msg, ")")
data := msg[start+1 : end]
fields := strings.Split(data, "|")
return strconv.Atoi(fields[3])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment