Nowy protokół wysyłania plików odkryty

Udało mi się odkryć protokół wysyłania plików w GG11. Dobra wiadomość jest taka, że zaimplementowałem własnego klienta wysyłającego plik. Zła jest taka, że do autoryzacji potrzebuje on tokenu, który jest wysyłany przez serwer Gadu-Gadu po udanym logowaniu w protokole GG11.

Oznacza to, że Pidgin (a także Kadu) będzie mógł wspierać nową metodę transferu plików dopiero, gdy libgadu będzie obsługiwał protokół GG11. Z kolei implementacja starego protokołu połączeń bezpośrednich przedstawia wątpliwą wartość, skoro producent oryginalnego klienta się z niego wycofuje.

Ogólne informacje

Wysyłanie plików w GG11 jest prawie całkowicie oparte o zapytania HTTP. Oryginalny klient łączy się z serwerem poprzez https, ale połączenia nieszyfrowane również działają. Jednak aby móc rozpocząć transfer, potrzebujemy ciągu IMToken, który jest wysyłany przez serwer Gadu-Gadu tuż po połączeniu się z wykorzystaniem nowego protokołu GG11 (GG10.5 nie otrzymuje takiego pakietu). Token jest ważny do chwili zakończenia połączenia GG.

Klient wysyłający plik tworzy „bilet”, będący identyfikatorem transferu. Po zaakceptowaniu go przez stronę odbierającą, zostaje on wgrany na GG dysk, do specjalnego katalogu (niedostępnego dla użytkownika). Następnie strona odbierająca pobiera ten plik z serwera.

Protokół nie informuje o postępie odbierania plików przez drugą stronę. To znaczy, że po wgraniu pliku na serwer, użytkownik jest informowany o pomyślnym przebiegu transferu.

Przykładowy skrypt PHP wysyłający plik wymaga jedynie podania wspomnianego tokenu, który musi być przechwycony (np. narzędziem Wireshark) z komunikacji oryginalnego klienta GG11. W czasie transferu pliku nie należy kończyć sesji GG11, aby nie wygasł token.

Wysyłanie pliku

Strona odbierająca musi korzystać z klienta zgodnego z GG11. Przebieg protokołu wysyłania pliku jest następujący. Szczegóły dotyczące sposobu przekazania i odbierania opisanych danych są zawarte w przykładowej implementacji.

#1 logujemy się za pomocą protokołu GG11.

#2 otrzymujemy pakiet zawierający IMToken (kod pakietu: 0x8C)

struct gg_imtoken
{
	char magic; /* 0x0A */
	char token_len;
	char token[token_len];
}

#3 autoryzujemy się (jednorazowo na sesję), wysyłając zapytanie PUT https://drive.mpa.gg.pl/signin. Podajemy:

  • swój numer GG;
  • otrzymany IMToken;
  • id klienta (?) – chyba jest stały dla danej wersji komunikatora, może tam być cokolwiek (serwer i tak przyjmie);
  • informacje o komputerze (hostname, system, wersja klienta GG, typ klienta – desktop).

W zamian otrzymamy ciastko sesji oraz security_token (dodatkowe zabezpieczenie, nie zmienia się w trakcie sesji).

#4 powiadamiamy o chęci wysłania pliku. Oryginalny klient robi to przed autoryzacją i otrzymuje błąd 401, po autoryzacji ponawia próbę. Wysyłamy zapytanie PUT https://drive.mpa.gg.pl/send_ticket, podając:

  • numer GG odbiorcy;
  • nazwę pliku i jego rozmiar;
  • security_token i ciastko sesji.

W zamian otrzymamy strukturę (json) SEND_TICKET_STATUS (nazwa robocza) z polami:

  • id: identyfikator transferu;
  • sender, recipient: numery GG nadawcy i odbiorcy;
  • file_name, file_size: nazwa i rozmiar pliku;
  • send_progress (float): postęp wysyłania pliku na serwer – liczba z zakresu [0, 1.0];
  • ack_status: co odbiorca postanowił zrobić z plikiem – możliwe wartości unknown, rejected, allowed;
  • send_mode: (?) zawsze normal;
  • send_status: stan wysyłania – możliwe wartości in_progress, completed.

#5 od tej pory możemy pytać o status naszego transferu, wysyłając zapytanie GET https://drive.mpa.gg.pl/send_ticket/[ticket_id]. Podajemy security_token i ciastko sesji, a otrzymujemy strukturę SEND_TICKET_STATUS.

#6 Jeżeli odbiorca zaakceptuje plik, możemy umieścić go na GG dysku zapytaniem PUT https://drive.mpa.gg.pl/me/file/outbox/[ticket_id]%2C[nazwa pliku]. Podajemy security_token, ciastko sesji, typ wysyłanych danych (plik) oraz oczywiście samą zawartość pliku. Nazwa pliku powinna być przekonwertowana przez funkcję podobną do rawurlencode z php. Oryginalny klient ma problem z odbieraniem plików zawierający znaki nie-alfanumeryczne – także, gdy został on również wysłany przez oryginalnego klienta.

Powiadomienia o statusie

Mniej szczegółowe informacje o statusie są również przesyłane standardowym protokołem GG11. W związku z tym prawdopodobnie nie ma potrzeby korzystania z zapytań do https://drive.mpa.gg.pl/send_ticket/[…]. Służą do tego następujące pakiety (nazwy robocze).

Otrzymanie powiadomienia, wspólne dla różnych usług nowego protokołu (między innymi są to powiadomienia GG dysku):

struct gg_mpa_notify /* ID: 0x84 */
{
    char magic1[3]; /* 08 02 10 */
    uint8_t seq;
    char magic2; /* 1a */
    uint8_t data_len;
    char magic3; /* 01, nie zawsze występuje */
    char data[data_len];
    uint8_t ntype_len;
    char ntype[ntype_len];
    char magic4[10];
}

Po otrzymaniu każdego z pakietów gg_mpa_notify, należy odesłać pakiet zawierający jego numer porządkowy:

struct gg_mpa_ack /* ID: 0x86 */
{
    char magic1[3]; /* 08 06 10 */
    uint8_t seq;
    char magic2[2]; /* 18 01 */
}

Możliwe typy (pole ntype) pakietów json, związanych z transferem:

  • edisc/send_ticket_changed: uproszczona wersja SEND_TICKET_STATUS, zawierająca pola id, ack_status, send_status;
  • edisc/scope_files_changed: wysyłane po wgraniu pliku na GG dysk.

Przykładowa kolejność pakietów dla poprawnego transferu:

  1. send_ticket_changed [ack_status: unknown, send_status: in_progress]
  2. send_ticket_changed [ack_status: allowed, send_status: in_progress]
  3. send_ticket_changed [ack_status: allowed, send_status: completed]
  4. scope_files_changed

Podsumowanie

Protokół transferu plików jest odkryty dostatecznie dobrze, aby móc go zaimplementować (czego dowodem jest przykładowy skrypt). Niestety nie wydaje się być możliwe uzyskanie ciągu IMToken korzystając ze starego protokołu GG, obsługiwanego przez libgadu. W związku z tym potrzebne jest włożenie dodatkowej pracy w dostosowanie tej biblioteki do nowego protokołu. Pozytywnym aspektem takich zmian będzie uzyskanie dużo bardziej niezawodnej metody przesyłania plików – teraz nie będzie zależna od widoczności klienta poza NAT.

1 komentarz do “Nowy protokół wysyłania plików odkryty

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.