Transferência de arquivos

ESTA PÁGINA ESTÁ OBSOLETA: LEIA developer/swarm:File transfer

Como é que o usamos?

Android

Quando você está falando com alguém no Android, você tem a possibilidade de enviar uma foto no seu dispositivo ou tirar uma foto com estes botões:

Botões_de_arquivo_do_Android

Nota: quando você envia um arquivo, o outro tem que aceitá-lo. Neste momento você verá ‘esperando peer’:

Android_esperando_par

Como funciona? (técnico)

Como funciona

Introdução

Jami é um aplicativo distribuído e tem que funcionar sem qualquer conexão com a internet. Então, transferência de arquivos também! Basicamente, usamos o mesmo método para realizar transferência de arquivos e chamadas, mas no TCP. Para resumir como funciona, podemos imaginar uma situação em que Alice (A) quer transferir um arquivo para Bob (B).

Em primeiro lugar, Alice solicitará uma conexão com Bob. Para isso, Jami está usando ICE (RFC 6544), um protocolo usado para negociar ligações entre pares. Alice enviará, em um pacote criptografado através do DHT, o IP de seu dispositivo. Então, quando Bob receber os ips de Alice, eles serão capazes de negociar um transporte onde Bob será capaz de enviar pacotes para Alice. A negociação pode ser bem-sucedida, mas se falhar, um servidor TURN será usado (o configurado nas configurações) para realizar a transferência. Se a negociação for bem-sucedida, Bob enviará seus ips para Alice para executar a negociação na outra direção.

Agora que o link TCP bidirecional está aqui, a próxima etapa será negociar um TLS 1.3 (geralmente um (TLS1.3)-(DHE-FFDHE8192)-(RSA-PSS-RSAE-SHA384)-(AES-256-GCM) quando escrevo estas linhas) entre Alice e Bob, e então Alice começará a transferir o arquivo.

A primeira parte será um pequeno cabeçalho para descrever o conteúdo do arquivo.

Processo

Envio de um arquivo

O método a seguir utilizado:

1. Um cliente chamará DataTransferFacade::sendFile() . DataTransferFacade é a classe correspondente à API exposta para os clientes. É usada para gerenciar uma visão das transferências de arquivos (as classes correspondentes são DataTransfer, IncomingFileTransfer, OutgoingFileTransfer e SubOutgoingFileTransfer). Este método pedirá à conta JamiAccount vinculada para solicitar uma conexão.

![Diagrama: Diagrama de classe de transferência de dados]images/file-transfer-dataatransfer-class-diagrama.png)

2. O método DhtPeerConnector: requestConnection() é acionado e cria uma conexão entre todos os dispositivos conectados do par (encontrados no DHT). O DhtPeerConnector é usado para gerenciar o loop de eventos principal que gerencia as conexões. Quando um dispositivo é encontrado, o event loop cria um ClientConnector (que gerencia a conexão de um dispositivo) e inicia o método process().

3. Este método é utilizado para iniciar o transporte ICE e colocar um PeerConnectionMsg (que contém a mensagem SDP, ver abaixo) no DHT e esperar uma resposta (DhtPeerConnector::Impl::onResponseMsg).

4. Em seguida, recebe-se uma resposta do DHT, que contém endereços públicos do dispositivo peer. Agora podemos negociar um link TLS (diretamente através do ICE, ou através do TURN como um fallback). Este TlsSocketEndpoint é dado ao objeto PeerConnection como saída e a transferência pode começar.

5.\ Quando o soquete TLS está pronto, a chamada de retorno DataTransferFacade::Impl::onConnectionRequestReply é chamada e um OutgoingFileTransfer é vinculado à PeerConnection como uma entrada. Esse OutgoingFileTransfer contém uma lista de SubOutgoingFileTransfer (um por dispositivo) em que cada subtransferência é uma transferência para um dispositivo. Fazemos isso para poder fornecer a visão mais otimista da transferência (se um contato tiver 3 dispositivos, onde o contato cancelou a transferência em um dispositivo, mas aceitou a transferência nos outros dois, a transferência mais avançada será mostrada).

6. O SubOutgoingFileTransfer primeiro transferirá o cabeçalho do arquivo, aguardará a aceitação por pares (uma mensagem “GO\n” no socket) e, em seguida, enviará o arquivo.

7. Se for recebida uma cancelamento do peer ou do cliente ou se a transferência de arquivos terminar, a conexão será fechada através de uma mensagem CANCEL no DhtPeerConnector::eventLoop() e os recursos serão liberados.

TLSsocketEndpoint

Recebimento de um arquivo

A mesma estrutura é usada para receber arquivos, mas o método muda um pouco:

  1. A classe JamiAccount é usada para receber mensagens do DHT, porque a primeira coisa recebida será a solicitação do DHT.

  2. Em seguida, esta mensagem é dada ao DhtPeerConnector: onRequestMessage() através do eventLoop.

  3. O DhtPeerConnector::Impl::answerToRequest tentará se conectar ao servidor TURN (se não estiver conectado) e inicializará o transporte ICE. Esse método abre 2 conexões de controle para um servidor TURN (uma para autorizar pares IPv4 e outra para pares IPv6, devido à RFC 6156) se ainda não estiver aberta e permite que os endereços públicos de pares se conectem. Em seguida, se o SDP recebido não contiver candidatos a ICE, usará o TURN e criará a resposta SDP para aguardar o par. Se o SDP contiver candidatos a ICE, o método tentará negociar o link (ou fallback no TURN) e, em seguida, responderá ao SDP (com candidatos a ICE ou não).

  4. Uma vez que os links estiverem prontos, como o remetente, um link TLS é negociado e dado à PeerConnection dada ao IncomingFileTransfer como entrada.

Re-pedir uma transferência de arquivo anterior

Conforme especificado em developer/swarm:Other mime types, as interações de transferência de dados agora são sincronizadas e armazenadas em conversas. Assim, um dispositivo pode detectar facilmente se um arquivo foi baixado ou não. Caso contrário, ele pode pedir a todos os membros da conversa que transmitam o arquivo novamente.

Para isso, o dispositivo enviará um json com o tipo de mime: application/data-transfer-request+json contendo conversation (id da conversa), interaction (interação relacionada), deviceId o dispositivo que recebe o arquivo.

O remetente agora verifica se o dispositivo é um dispositivo do peer anunciado e que o dispositivo é um membro da conversa, e pode enviar o arquivo através de uma transferência de arquivo clássica.

O receptor pode agora aceitar a primeira transferência de entrada, baixar o arquivo e verificar que o sha3sum é correto.

Esquema

Diagrama: diagrama do esquema principal

SDP enviado através do DHT
0d04b932
7c33834e7cf944bf0e367b47
H6e6ca682 1 TCP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 50693 typ host tcptype passive
H6e6ca682 1 TCP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 9 typ host tcptype active
H42c1b577 1 TCP 2130706431 fe80::9eb6:d0ff:fee7:1412 50693 typ host tcptype passive
H42c1b577 1 TCP 2130706431 fe80::9eb6:d0ff:fee7:1412 9 typ host tcptype active
Hc0a8007e 1 TCP 2130706431 192.168.0.123 42751 typ host tcptype passive
Hc0a8007e 1 TCP 2130706431 192.168.0.123 9 typ host tcptype active
Sc0a8007e 1 TCP 1694498815 X.X.X.X 42751 typ srflx tcptype passive
Z.Z.Z.Z:YYYY
A.A.A.A:YYYY

Onde 0d04b932 é o ufrag e 7c33834e7cf944bf0e367b47 a senha da sessão ICE. 2130706431 e 1694498815 são a prioridade dos candidatos. 192.168.0.126 42751 tipo de hospedeiro tcptype passivo é um candidato hospedeiro passivo e 1694498815 X.X.X 42751 tipo srflx tcptype passivo um hospedeiro passivo que reflete o IP público (mapeado através de UPnP, por exemplo).

Dispositivos múltiplos

Um usuário pode vincular sua conta a vários dispositivos. Portanto, precisamos implementar a transferência quando um usuário envia um arquivo para um contato que tem vários dispositivos vinculados a essa conta.

Primeira abordagem

A primeira abordagem foi enviar uma solicitação através do DHT para todos os dispositivos e os primeiros dispositivos que respondem obter o arquivo para transferir.

abordagem atual

Agora, ainda enviamos uma solicitação para todos os dispositivos. A diferença é que todos os dispositivos terão a notificação para receber um arquivo e podem aceitar/recusar a transferência. A maior parte do código para isso está em data_transfer.cpp.

Agora (desde https://review.jami.net/c/jami-daemon/+/9327), quando um usuário enviar um arquivo, ele solicitará uma PeerConnection com todos os dispositivos pares. E para todas as conexões, anexamos um novo fluxo de entrada para poder aceitar/recusar/cancelar cada transferência separadamente.

Em data_transfer.cpp definimos a classe OptimisticMetaOutgoingInfo que representam a visão otimista para mostrar ao cliente. É otimista porque se um contato aceita uma transferência em um dispositivo e recusa em outros, esta classe mostrará a transferência de arquivo em curso. E só mostrará um erro se todos os dispositivos recusarem a transferência.

Essa classe está vinculada a SubOutgoingFileTransfer, que representa o estado de uma transferência com um dispositivo. Os clientes terão a capacidade de mostrar uma subtransferência em vez da otimista mais tarde (ver lista PARA FAZER).

Usando outro servidor TURN

Atualmente, o servidor TURN predefinido é turn.jami.net. Mas pode alojar o seu próprio servidor TURN. Por exemplo, executando um servidor coturn.

sudo turnserver -a -v -n -u usuário:password -r "realm"

Em seguida, você pode configurar o servidor TURN nas configurações avançadas do aplicativo.

Nota: isso requer alguns conhecimentos técnicos. Além disso, o servidor TURN deve ver o mesmo endereço IP do seu nó como o nó de destino ou a conexão de pares falhará (porque a autorização será incorreta)

Lista de PARA FAZER

  1. Usar libtorrent?

  2. Mostre o status de subtransferências para arquivos saídos