44 "bufio"
55 "crypto/rand"
66 "crypto/sha256"
7+ "crypto/tls"
78 "encoding/hex"
89 "fmt"
910 "io"
@@ -26,13 +27,16 @@ type Conn struct {
2627 auth Auth
2728 logger * Logger
2829 server * Server
29- sessionId string
30+ tlsConfig * tls.Config
31+ sessionID string
3032 namePrefix string
3133 reqUser string
3234 user string
3335 renameFrom string
3436 lastFilePos int64
3537 appendData bool
38+ closed bool
39+ tls bool
3640}
3741
3842func (conn * Conn ) LoginUser () string {
@@ -44,7 +48,7 @@ func (conn *Conn) IsLogin() bool {
4448}
4549
4650// returns a random 20 char string that can be used as a unique session ID
47- func newSessionId () string {
51+ func newSessionID () string {
4852 hash := sha256 .New ()
4953 _ , err := io .CopyN (hash , rand .Reader , 50 )
5054 if err != nil {
@@ -76,6 +80,11 @@ func (conn *Conn) Serve() {
7680 break
7781 }
7882 conn .receiveLine (line )
83+ // QUIT command closes connection, break to avoid error on reading from
84+ // closed socket
85+ if conn .closed == true {
86+ break
87+ }
7988 }
8089 conn .Close ()
8190 conn .logger .Print ("Connection Terminated" )
@@ -84,32 +93,46 @@ func (conn *Conn) Serve() {
8493// Close will manually close this connection, even if the client isn't ready.
8594func (conn * Conn ) Close () {
8695 conn .conn .Close ()
96+ conn .closed = true
8797 if conn .dataConn != nil {
8898 conn .dataConn .Close ()
8999 conn .dataConn = nil
90100 }
91101}
92102
103+ func (conn * Conn ) upgradeToTLS () error {
104+ conn .logger .Print ("Upgrading connectiion to TLS" )
105+ tlsConn := tls .Server (conn .conn , conn .tlsConfig )
106+ err := tlsConn .Handshake ()
107+ if err == nil {
108+ conn .conn = tlsConn
109+ conn .controlReader = bufio .NewReader (tlsConn )
110+ conn .controlWriter = bufio .NewWriter (tlsConn )
111+ conn .tls = true
112+ }
113+ return err
114+ }
115+
93116// receiveLine accepts a single line FTP command and co-ordinates an
94117// appropriate response.
95- func (Conn * Conn ) receiveLine (line string ) {
96- command , param := Conn .parseLine (line )
97- Conn .logger .PrintCommand (command , param )
118+ func (conn * Conn ) receiveLine (line string ) {
119+ command , param := conn .parseLine (line )
120+ conn .logger .PrintCommand (command , param )
98121 cmdObj := commands [strings .ToUpper (command )]
99122 if cmdObj == nil {
100- Conn .writeMessage (500 , "Command not found" )
123+ conn .writeMessage (500 , "Command not found" )
101124 return
102125 }
103126 if cmdObj .RequireParam () && param == "" {
104- Conn .writeMessage (553 , "action aborted, required param missing" )
105- } else if cmdObj .RequireAuth () && Conn .user == "" {
106- Conn .writeMessage (530 , "not logged in" )
127+ conn .writeMessage (553 , "action aborted, required param missing" )
128+ } else if cmdObj .RequireAuth () && conn .user == "" {
129+ conn .writeMessage (530 , "not logged in" )
107130 } else {
108- cmdObj .Execute (Conn , param )
131+ cmdObj .Execute (conn , param )
109132 }
110133}
111134
112- func (Conn * Conn ) parseLine (line string ) (string , string ) {
135+ func (conn * Conn ) parseLine (line string ) (string , string ) {
113136 params := strings .SplitN (strings .Trim (line , "\r \n " ), " " , 2 )
114137 if len (params ) == 1 {
115138 return params [0 ], ""
@@ -118,11 +141,11 @@ func (Conn *Conn) parseLine(line string) (string, string) {
118141}
119142
120143// writeMessage will send a standard FTP response back to the client.
121- func (Conn * Conn ) writeMessage (code int , message string ) (wrote int , err error ) {
122- Conn .logger .PrintResponse (code , message )
144+ func (conn * Conn ) writeMessage (code int , message string ) (wrote int , err error ) {
145+ conn .logger .PrintResponse (code , message )
123146 line := fmt .Sprintf ("%d %s\r \n " , code , message )
124- wrote , err = Conn .controlWriter .WriteString (line )
125- Conn .controlWriter .Flush ()
147+ wrote , err = conn .controlWriter .WriteString (line )
148+ conn .controlWriter .Flush ()
126149 return
127150}
128151
@@ -143,43 +166,43 @@ func (Conn *Conn) writeMessage(code int, message string) (wrote int, err error)
143166// The driver implementation is responsible for deciding how to treat this path.
144167// Obviously they MUST NOT just read the path off disk. The probably want to
145168// prefix the path with something to scope the users access to a sandbox.
146- func (Conn * Conn ) buildPath (filename string ) (fullPath string ) {
169+ func (conn * Conn ) buildPath (filename string ) (fullPath string ) {
147170 if len (filename ) > 0 && filename [0 :1 ] == "/" {
148171 fullPath = filepath .Clean (filename )
149172 } else if len (filename ) > 0 && filename != "-a" {
150- fullPath = filepath .Clean (Conn .namePrefix + "/" + filename )
173+ fullPath = filepath .Clean (conn .namePrefix + "/" + filename )
151174 } else {
152- fullPath = filepath .Clean (Conn .namePrefix )
175+ fullPath = filepath .Clean (conn .namePrefix )
153176 }
154177 fullPath = strings .Replace (fullPath , "//" , "/" , - 1 )
155178 return
156179}
157180
158181// sendOutofbandData will send a string to the client via the currently open
159182// data socket. Assumes the socket is open and ready to be used.
160- func (Conn * Conn ) sendOutofbandData (data []byte ) {
183+ func (conn * Conn ) sendOutofbandData (data []byte ) {
161184 bytes := len (data )
162- if Conn .dataConn != nil {
163- Conn .dataConn .Write (data )
164- Conn .dataConn .Close ()
165- Conn .dataConn = nil
185+ if conn .dataConn != nil {
186+ conn .dataConn .Write (data )
187+ conn .dataConn .Close ()
188+ conn .dataConn = nil
166189 }
167190 message := "Closing data connection, sent " + strconv .Itoa (bytes ) + " bytes"
168- Conn .writeMessage (226 , message )
191+ conn .writeMessage (226 , message )
169192}
170193
171- func (Conn * Conn ) sendOutofBandDataWriter (data io.ReadCloser ) error {
172- Conn .lastFilePos = 0
173- bytes , err := io .Copy (Conn .dataConn , data )
194+ func (conn * Conn ) sendOutofBandDataWriter (data io.ReadCloser ) error {
195+ conn .lastFilePos = 0
196+ bytes , err := io .Copy (conn .dataConn , data )
174197 if err != nil {
175- Conn .dataConn .Close ()
176- Conn .dataConn = nil
198+ conn .dataConn .Close ()
199+ conn .dataConn = nil
177200 return err
178201 }
179202 message := "Closing data connection, sent " + strconv .Itoa (int (bytes )) + " bytes"
180- Conn .writeMessage (226 , message )
181- Conn .dataConn .Close ()
182- Conn .dataConn = nil
203+ conn .writeMessage (226 , message )
204+ conn .dataConn .Close ()
205+ conn .dataConn = nil
183206
184207 return nil
185208}
0 commit comments