Commits

Ross Light committed d19b828

Support Passive on IPv6

Comments (0)

Files changed (3)

 
 // Passive opens a new passive data port.
 func (client *Client) Passive() (io.ReadWriteCloser, os.Error) {
-	// TODO(ross): EPSV for IPv6
-	reply, err := client.Do("PASV")
-	if err != nil {
-		return nil, err
-	} else if reply.Code != CodePassive {
-		return nil, reply
+	var reply Reply
+	var addr *net.TCPAddr
+	var err os.Error
+
+	switch client.c.RemoteAddr().Network() {
+	case "tcp6":
+		reply, err = client.Do("EPSV")
+		if err != nil {
+			return nil, err
+		} else if reply.Code != CodeExtendedPassive {
+			return nil, reply
+		}
+
+		port, err := parseEpsvReply(reply.Msg)
+		if err != nil {
+			return nil, err
+		}
+
+		addr = &net.TCPAddr{
+			IP:   client.c.RemoteAddr().(*net.TCPAddr).IP,
+			Port: port,
+		}
+	default:
+		reply, err = client.Do("PASV")
+		if err != nil {
+			return nil, err
+		} else if reply.Code != CodePassive {
+			return nil, reply
+		}
+
+		addr, err = parsePasvReply(reply.Msg)
+		if err != nil {
+			return nil, err
+		}
 	}
 
-	addr, err := parsePasvReply(reply.Msg)
-	if err != nil {
-		return nil, err
-	}
-
-	return net.DialTCP("tcp4", nil, addr)
+	return net.DialTCP("tcp", nil, addr)
 }
 
 var pasvRegexp = regexp.MustCompile(`([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+)`)
 	}, nil
 }
 
+const (
+	epsvStart = "(|||"
+	epsvEnd   = "|)"
+)
+
+func parseEpsvReply(msg string) (port int, err os.Error) {
+	start := strings.LastIndex(msg, epsvStart)
+	if start == -1 {
+		return 0, os.NewError("EPSV reply provided no port")
+	}
+	start += len(epsvStart)
+
+	end := strings.LastIndex(msg, epsvEnd)
+	if end == -1 || end <= start {
+		return 0, os.NewError("EPSV reply provided no port")
+	}
+
+	return strconv.Atoi(msg[start:end])
+}
+
 // transfer sends a command and opens a new passive data connection.
 func (client *Client) transfer(command, dataType string) (io.ReadWriteCloser, os.Error) {
 	// Set type
 		t.Errorf("addr.Port = %v (expected %v)", addr.Port, expectedPort)
 	}
 }
+
+func TestEpsvReply(t *testing.T) {
+	const expectedPort = 1031
+	port, err := parseEpsvReply("229 Entering Extended Passive Mode. (|||1031|)")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if port != expectedPort {
+		t.Errorf("port = %v (expected %v)", port, expectedPort)
+	}
+}
 	CodeNoTransfer      = 225
 	CodeClosingData     = 226
 	CodePassive         = 227
+	CodeExtendedPassive = 229
 	CodeLoggedIn        = 230
 	CodeActionOkay      = 250
 	CodeCreated         = 257