We study the MMP protocol (Mail.ru agent) and write an alternative client

    It's not a secret that Mail.ru Agent has become a fairly popular IM project. Here you have support for ICQ, XMPP, voice calls and even sending SMS, only Mail.ru has completely forgotten about the developers.
    The official documentation of the Mail.ru data exchange protocol. The agent describes protocol version 1.7 implemented in 2008. At the moment, the server uses protocol version 1.24.

    Bit of theory


    At first glance, writing a network client is not difficult, but there are many pitfalls in network programming. Without understanding the details of TCP / IP, it is almost impossible to write an effective and stable application.

    Transmit Data Integrity

    As you know, TCP is a streaming protocol, and although data is transmitted in IP packets, the packet size is not directly related to the amount of data transmitted by TCP. Therefore, it is impossible to say with certainty that when recv is called, we will get the specified number of bytes.
    To get the data of a given length, I use this function,
    #define SEND 0
    #define RECV 1
    int (__stdcall *tcp_func)(SOCKET s,char* buf,int len,int flags);
    // функция гарантирует прием/отправку данных заданной длинны len
    int tcp_rs(unsigned char type,SOCKET s, void *buf, int len, int flags) {
    	int total = 0;
    	int n;
    	*(void* *)&tcp_func=(type==SEND)?&send:&recv;
    	while(total < len) {
    		n = tcp_func(s, (char *)buf+total, len-total, flags);
    		if(n>0) { total += n; }
    		else if(n == 0) { 
    			closesocket(s);
    			return 0;
    		}
    		else {
    			n=WSAGetLastError();
    			closesocket(s);
    			return (!n+1);
    		}
    	}
    	return total;
    }

    which, if successful, returns the number of received / transmitted bytes equal to len, 0 if the connection was disconnected or closed and (minus) the error number, if the call to the send / recv function fails.

    Network outages

    It is also necessary to remember that TCP does not poll the connection. In the case of blocking sockets, when the server crashes (disconnection, failure), we will wait for an answer “forever”, the program simply “freezes”.
    One way to determine if a connection is broken is through a keep-alive timer.
    #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
    #pragma pack(push,1) // отключаем выравнивание
    typedef struct tcp_keepalive {
    	DWORD  onoff;
    	DWORD  keepalivetime;
    	DWORD  keepaliveinterval;
    } tcp_keepalive;
    #pragma pack(pop)
    //пример изменения интервалов времени посылки keep-alive
    struct tcp_keepalive alive;
    DWORD  dwSize;
    alive.onoff = 1;
    alive.keepalivetime = 5000;
    alive.keepaliveinterval = 1000;
    WSAIoctl(my_sock, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),NULL, 0, &dwSize, NULL, NULL);
    

    In our case, if the connection is not active within 5 seconds, a service message will be sent, if there is no answer to it, the connection will be closed.

    About Protocol


    MMP binary asynchronous protocol. Binary means that data is transmitted in the form of packets of a certain structure:
    
    // заколовок пакета
    typedef struct mrim_packet_header_t
    {
        unsigned int      magic;		// Magic
        unsigned int      proto;		// Версия протокола
        unsigned int      seq;		// Sequence
        unsigned int      msg;		// Тип пакета
        unsigned int      dlen; 		// Длина данных
        unsigned int	from;		// Адрес отправителя
        unsigned int	fromport;	// Порт отправителя
        unsigned char	reserved[16];	// Зарезервировано
    }
    mrim_packet_header_t;
    // структура описываемая в документации, похожа на MFC, Delphi строки 
    typedef struct LPS {
    	unsigned int len;
    	unsigned char *str;
    } LPS;
    

    Asynchrony here is characterized by the fact that the server, maintaining a constant connection at various time intervals, sends data packets to the client, receiving which the client can (and in some cases must) respond and send a response to the server.

    Initializes Connect client, before it is necessary to get the address of a "free» MMP server in text format the ip: port , just by connecting to the address mrim.mail.ru . The official client version 5.9 uses the following ports for connection: 2024, 80, 5190, 1863, 25, 110, 443.
    As stated in the official documentation, after connecting to the recommended address, the client must send the MRIM_CS_HELLO packet, wait for MRIM_CS_HELLO_ACK, and then send the authorization packet, then the fun begins.

    In fact

    Starting from version 1.22 (Mail.ru agent 5.7), the authorization method has changed. Now for authorization it is necessary to send a packet 0x1078 (MRIM_CS_LOGIN3) with

    LPS parameters ## login ## email of the authorized user
    LPS ## md5 password ## password encrypted in md5
    FFFFFFFF
    and 1391 bytes identifying Mail.ru client

    Currently (protocol version 1.24) protocol supports mandatory encryption. After receiving the MRIM_CS_HELLO_ACK packet, the client sends a 0x1086 packet and receives a 0x1087 response, after which SSL connection is initialized.
    But so far, no one has forbidden us to use earlier versions of the protocol.

    An important feature of the client’s work is that the client can send its requests only after receiving from the server the package MRIM_CS_CONTACT_LIST2, which in turn is sent after successful authorization.

    Projects


    All MMP client code would take up a lot of space, so I suggest you download and study it yourself. The MMPclient_sample.25.04.2011.rar archive contains C source codes and a Visual Studio project.
    UPD: github source

    To study the protocol, a small SOCKS 5 server was written. It allows you to conveniently track the chain of messages of the client and server. Server sources and the project can be downloaded here.

    And:

    References


    All versions of Mail.ru Agent.
    Official documentation on the protocol.

    Tutorial for playing WINSOCK.
    Detecting a TCP connection.
    Effective TCP / IP programming.

    Also popular now: