Acontece que a resposta realmente tem a ver com a biblioteca FTP que eu estava usando. Eu pensei com certeza que isso não era um problema de codificação, mas depois que eu cheirei como a biblioteca está criando a solicitação, acho que encontrei a resposta.
Aqui está a seção relevante usando o ILSpy (eu coloquei o espaço em torno da linha mais importante):
private Socket CreateDataSocketActive()
{
Socket socket = new Socket(2, 1, 6);
IPHostEntry iPHostEntry = Dns.Resolve(Dns.GetHostName());
IPEndPoint iPEndPoint = new IPEndPoint(iPHostEntry.get_AddressList()[0], 0);
socket.Bind(iPEndPoint);
socket.Listen(5);
int port = ((IPEndPoint)socket.get_LocalEndPoint()).get_Port();
IPAddress address = ((IPEndPoint)socket.get_LocalEndPoint()).get_Address();
this.SetDataPort((IPEndPoint)socket.get_LocalEndPoint());
return socket;
}
Parece que a biblioteca está criando o endpoint IP simplesmente pegando o primeiro endereço na lista de endereços IP disponíveis. Para a maioria das soluções, isso é provavelmente aceitável, mas como tenho várias NICs no meu servidor, não funciona para mim.
Sei que usando o FTP.exe, o servidor aceitará conexões de ambas as sub-redes IP da LAN, desde que o comando PORT seja emitido usando o mesmo endereço IP da LAN que a origem da solicitação.
Eu estou supondo que há algum tipo de validação feita pelo servidor FTP que compara os dois endereços para verificar se eles são os mesmos. Caso contrário, eu poderia teoricamente transferir um arquivo para algum outro dispositivo em uma rede completamente diferente, uma vez que eu fiz login no site FTP.
Agora eu só tenho que descobrir uma solução alternativa, mas isso deve ser apenas uma questão de encontrar uma nova biblioteca ou rolar a minha. Se ajudar alguém que possa ter o mesmo problema, a biblioteca que eu estava usando era com.enterprisedt.net.ftp.