Skip to content

Commit e57293e

Browse files
fclairambsyncplify
andauthored
Implementing all 4 typical "messages" as in proftpd and other commercial FTP servers by @syncplify (#435)
* Implemented all 4 "msgs" as in proftpd and other commercial FTP servers * Fixed 2 out of 3 issues as per Drakkan's request * Added GetPostAuthMessage via MainDriverExtensionPostAuthMessage interface * Trying to make wsl happy * Rewriting some _old_ code around the handlePASS method * Cleaner QUIT handler --------- Co-authored-by: Syncplify <fjodr@syncplify.me> Co-authored-by: syncplify <info@syncplify.me>
1 parent ad78d02 commit e57293e

6 files changed

Lines changed: 137 additions & 8 deletions

File tree

driver.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ type MainDriverExtensionUserVerifier interface {
5858
PreAuthUser(cc ClientContext, user string) error
5959
}
6060

61+
// MainDriverExtensionPostAuthMessage is an extension that allows to send a message
62+
// after the authentication
63+
type MainDriverExtensionPostAuthMessage interface {
64+
// PostAuthMessage is called after the authentication
65+
PostAuthMessage(cc ClientContext, user string, authErr error) string
66+
}
67+
68+
// MainDriverExtensionQuitMessage is an extension that allows to control the quit message
69+
type MainDriverExtensionQuitMessage interface {
70+
// QuitMessage returns the message to display when the user quits the server
71+
QuitMessage() string
72+
}
73+
6174
// ClientDriver is the base FS implementation that allows to manipulate files
6275
type ClientDriver interface {
6376
afero.Fs

driver_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ type TestServerDriver struct {
103103
TLSVerificationReply tlsVerificationReply
104104
errPassiveListener error
105105
TLSRequirement TLSRequirement
106+
customAuthMessage bool
107+
customQuitMessage bool
106108
}
107109

108110
// TestClientDriver defines a minimal serverftp client driver
@@ -229,11 +231,36 @@ func (driver *TestServerDriver) AuthUser(_ ClientContext, user, pass string) (Cl
229231
clientdriver := NewTestClientDriver(driver)
230232

231233
return clientdriver, nil
234+
} else if user == "nil" && pass == "nil" {
235+
// Definitely a bad behavior (but can be done on the driver side)
236+
return nil, nil
232237
}
233238

234239
return nil, errBadUserNameOrPassword
235240
}
236241

242+
// PostAuthMessage returns a message displayed after authentication
243+
func (driver *TestServerDriver) PostAuthMessage(_ ClientContext, _ string, authErr error) string {
244+
if !driver.customAuthMessage {
245+
return ""
246+
}
247+
248+
if authErr != nil {
249+
return "You are not welcome here"
250+
}
251+
252+
return "Welcome to the FTP Server"
253+
}
254+
255+
// QuitMessage returns a goodbye message
256+
func (driver *TestServerDriver) QuitMessage() string {
257+
if !driver.customQuitMessage {
258+
return ""
259+
}
260+
261+
return "Sayonara, bye bye!"
262+
}
263+
237264
// ClientDisconnected is called when the user disconnects
238265
func (driver *TestServerDriver) ClientDisconnected(cc ClientContext) {
239266
driver.clientMU.Lock()

handle_auth.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,31 @@ func (c *clientHandler) handleUSER(param string) error {
5757
// Handle the "PASS" command
5858
func (c *clientHandler) handlePASS(param string) error {
5959
var err error
60+
var msg string
6061
c.driver, err = c.server.driver.AuthUser(c, c.user, param)
6162

63+
dpa, ok := c.server.driver.(MainDriverExtensionPostAuthMessage)
64+
if ok {
65+
msg = dpa.PostAuthMessage(c, c.user, err)
66+
}
67+
6268
switch {
63-
case err == nil:
64-
c.writeMessage(StatusUserLoggedIn, "Password ok, continue")
65-
case err != nil:
66-
c.writeMessage(StatusNotLoggedIn, fmt.Sprintf("Authentication problem: %v", err))
69+
case err == nil && c.driver == nil:
70+
c.writeMessage(StatusNotLoggedIn, "Unexpected exception (driver is nil)")
6771
c.disconnect()
68-
default:
69-
c.writeMessage(StatusNotLoggedIn, "I can't deal with you (nil driver)")
72+
case err != nil:
73+
if msg == "" {
74+
msg = fmt.Sprintf("Authentication error: %v", err)
75+
}
76+
77+
c.writeMessage(StatusNotLoggedIn, msg)
7078
c.disconnect()
79+
case err == nil:
80+
if msg == "" {
81+
msg = "Password ok, continue"
82+
}
83+
84+
c.writeMessage(StatusUserLoggedIn, msg)
7185
}
7286

7387
return nil

handle_auth_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,42 @@ func TestLoginFailure(t *testing.T) {
9090
require.Error(t, err, "We should have failed to login")
9191
}
9292

93+
func TestLoginCustom(t *testing.T) {
94+
s := NewTestServerWithDriver(t, &TestServerDriver{Debug: true, customAuthMessage: true})
95+
r := require.New(t)
96+
97+
conf := goftp.Config{
98+
User: authUser,
99+
Password: authPass + "_wrong",
100+
}
101+
102+
c, err := goftp.DialConfig(conf, s.Addr())
103+
r.NoError(err, "Couldn't connect")
104+
105+
defer func() { panicOnError(c.Close()) }()
106+
107+
_, err = c.OpenRawConn()
108+
r.Error(err, "We should have failed to login")
109+
}
110+
111+
func TestLoginNil(t *testing.T) {
112+
s := NewTestServer(t, true)
113+
r := require.New(t)
114+
115+
conf := goftp.Config{
116+
User: "nil",
117+
Password: "nil",
118+
}
119+
120+
c, err := goftp.DialConfig(conf, s.Addr())
121+
require.NoError(t, err, "Couldn't connect")
122+
123+
defer func() { panicOnError(c.Close()) }()
124+
125+
_, err = c.OpenRawConn()
126+
r.Error(err)
127+
}
128+
93129
func TestAuthTLS(t *testing.T) {
94130
s := NewTestServerWithDriver(t, &TestServerDriver{
95131
Debug: false,

handle_misc.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
var errUnknowHash = errors.New("unknown hash algorithm")
1313

14-
func (c *clientHandler) handleAUTH(param string) error {
14+
func (c *clientHandler) handleAUTH(_ string) error {
1515
if tlsConfig, err := c.server.driver.GetTLSConfig(); err == nil {
1616
c.writeMessage(StatusAuthAccepted, "AUTH command ok. Expecting TLS Negotiation.")
1717
c.conn = tls.Server(c.conn, tlsConfig)
@@ -274,7 +274,16 @@ func (c *clientHandler) handleTYPE(param string) error {
274274

275275
func (c *clientHandler) handleQUIT(param string) error {
276276
c.transferWg.Wait()
277-
c.writeMessage(StatusClosingControlConn, "Goodbye")
277+
278+
var msg string
279+
280+
if quitter, ok := c.server.driver.(MainDriverExtensionQuitMessage); ok {
281+
msg = quitter.QuitMessage()
282+
} else {
283+
msg = "Goodbye"
284+
}
285+
286+
c.writeMessage(StatusClosingControlConn, msg)
278287
c.disconnect()
279288
c.reader = nil
280289

handle_misc_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,36 @@ func TestQuit(t *testing.T) {
275275
require.Equal(t, StatusClosingControlConn, rc)
276276
}
277277

278+
func TestQuitWithCustomMessage(_t *testing.T) {
279+
s := NewTestServerWithDriver(_t, &TestServerDriver{
280+
Debug: true,
281+
TLS: true,
282+
customQuitMessage: true,
283+
})
284+
t := require.New(_t)
285+
conf := goftp.Config{
286+
User: authUser,
287+
Password: authPass,
288+
TLSConfig: &tls.Config{
289+
//nolint:gosec
290+
InsecureSkipVerify: true,
291+
},
292+
TLSMode: goftp.TLSExplicit,
293+
}
294+
c, err := goftp.DialConfig(conf, s.Addr())
295+
t.NoError(err, "Couldn't connect")
296+
297+
defer func() { panicOnError(c.Close()) }()
298+
299+
raw, err := c.OpenRawConn()
300+
t.NoError(err, "Couldn't open raw connection")
301+
302+
rc, msg, err := raw.SendCommand("QUIT")
303+
t.NoError(err)
304+
t.Equal(StatusClosingControlConn, rc)
305+
t.Equal("Sayonara, bye bye!", msg)
306+
}
307+
278308
func TestQuitWithTransferInProgress(t *testing.T) {
279309
s := NewTestServerWithDriver(t, &TestServerDriver{
280310
Debug: false,

0 commit comments

Comments
 (0)