The truth behind SID_PING

Started by brew, August 06, 2010, 02:52:06 AM

Previous topic - Next topic

brew

SID_PING, better known as "the 0x25 packet", does more than just a ping.

Pinging is sent in 20-second intervals, provided the initial ping packet was responded to correctly (not spoofed to be -1 or 0) AND SID_NULL packets are sent on a regular interval.
If the regular interval of null packets is broken, battle.net detects it as a connection that's "almost lost", and sends a ping packet once every 5 seconds until they have been responded to and the interval of null packets is reestablished.
In order to get an accurate ping, both the client and server temporarily disable the Nagle algorithm since a packet could be held up as long as 5 seconds with a rather idle connection (do note that the constant value of 1 is in fact TCP_NODELAY with level == IPPROTO_TCP, whereas 1 is SO_DEBUG with the level SOL_SOCKET):

.text:1901D4F0 Send0x25        proc near               ; CODE XREF: ServerThreadProc+2F8p
.text:1901D4F0
.text:1901D4F0 optval          = byte ptr -4
.text:1901D4F0
.text:1901D4F0                 push    ebp
.text:1901D4F1                 mov     ebp, esp
.text:1901D4F3                 push    ecx
.text:1901D4F4                 test    edi, edi
.text:1901D4F6                 jz      short loc_1901D53B
.text:1901D4F8                 mov     ecx, sck
.text:1901D4FE                 push    esi
.text:1901D4FF                 mov     esi, ds:setsockopt
.text:1901D505                 push    4               ; optlen
.text:1901D507                 lea     eax, [ebp+optval]
.text:1901D50A                 push    eax             ; optval
.text:1901D50B                 push    1               ; optname == TCP_NODELAY
.text:1901D50D                 push    6               ; level == IPPROTO_TCP
.text:1901D50F                 push    ecx             ; s
.text:1901D510                 mov     dword ptr [ebp+optval], 1
.text:1901D517                 call    esi ; setsockopt
.text:1901D519                 push    edi
.text:1901D51A                 push    25h
.text:1901D51C                 call    SendPacket
.text:1901D521                 mov     eax, sck
.text:1901D526                 push    4               ; optlen
.text:1901D528                 lea     edx, [ebp+optval]
.text:1901D52B                 push    edx             ; optval
.text:1901D52C                 push    1               ; optname
.text:1901D52E                 push    6               ; level
.text:1901D530                 push    eax             ; s
.text:1901D531                 mov     dword ptr [ebp+optval], 0
.text:1901D538                 call    esi ; setsockopt
.text:1901D53A                 pop     esi
.text:1901D53B
.text:1901D53B loc_1901D53B:                           ; CODE XREF: Send0x25+6j
.text:1901D53B                 mov     esp, ebp
.text:1901D53D                 pop     ebp
.text:1901D53E                 retn
.text:1901D53E Send0x25        endp


This creates the 'other' purpose I was talking about before, to synchronize the buffer used by recv in ServerThreadProc:


          case 0x21:
            Send0x25(packetid_minus_4, pktdata, pktlen - 4);
            pkg_align_variance = *(_DWORD *)lpTmp;
            ws_buf = ws_buf_base;
            break;

Code like this suggests that battle.snp knows the server's send buffer is flushed after an 0x25 send. SID_PING is not the only packet to have this buffer reset, though. Others are 0x28 SID_LOGONCHALLENGE, and 0x68 SID_FRIENDSREMOVE.
This means that you are guaranteed to never have a packet clumped after an 0x25, 0x28, or 0x68. Cool stuff.

Muchos kudos to Hdx, he pointed out the massive screwup I made earlier of thinking that 1 was SO_DEBUG here. Things make a lot more sense now.