• Welcome to Valhalla Legends Archive.
 

Help with initial logon sequence and message 0x50 (C++)

Started by Win32, May 27, 2006, 08:38 AM

Previous topic - Next topic

Win32

Hi,
First question I have is, what is the very first message to be sent to the server? I gather it's the protocol identifier byte, but I'm not certain. And in what format is the protocol identifier byte sent to the server?

Secondly, does my construct (below) of the 0x50 message appear correct?

P.S; After awhile I'll receive 4 bytes, this will occur a variable number of times with the interval being around 30 seconds.


struct SID_AUTH_INFO
{
Byte  Header;
Byte  MessageId;
Word  wLength;
Dword dwProtocolId;
Dword dwPlatformId;
Dword dwProductId;
Dword dwVersion;
Dword dwProductLanguage;
Dword dwLocalIp;
Dword dwTimeZoneBias;
Dword dwLocaleId;
Dword dwLanguageId;
Char  strCountryAbbrv[4];
Char  strCountry[10];
} Packet;


Packet.Header            = 0xFF;
Packet.MessageId         = 0x50;
Packet.wLength           = sizeof(SID_AUTH_INFO);
Packet.dwProtocolId      = 0x0;
Packet.dwPlatformId      = '68XI';
Packet.dwProductId       = 'RATS';
Packet.dwVersion         = 0xCD;
Packet.dwProductLanguage = 0;
Packet.dwLocalIp         = inet_addr("192.168.0.2");
Packet.dwTimeZoneBias    = 600;
Packet.dwLocaleId        = 0;
Packet.dwLanguageId      = (Dword) GetUserDefaultLangID();
strcpy(Packet.strCountryAbbrv, "Aus");
strcpy(Packet.strCountry, "Australia");



I've been running some simple tests which look like the following; (I apologize for the sloppness of the code within main, just debugging)


void main()
{
bThreadIsIdle = FALSE;
bSuspendThread = FALSE;


WSAData WSI;
ZeroMemory(&BnetSockAddress, sizeof(SOCKADDR_IN));
ZeroMemory(&BnetHostAddress, sizeof(SOCKADDR_IN));

BnetSockAddress.sin_family = AF_INET;

BnetHostAddress.sin_family = AF_INET;
BnetHostAddress.sin_addr.s_addr = inet_addr("63.240.202.130");
BnetHostAddress.sin_port = htons(6112);

WSAStartup(MAKEWORD(2, 2), &WSI);


struct SID_AUTH_INFO
{
Byte  Header;
Byte  MessageId;
Word  wLength;
Dword dwProtocolId;
Dword dwPlatformId;
Dword dwProductId;
Dword dwVersion;
Dword dwProductLanguage;
Dword dwLocalIp;
Dword dwTimeZoneBias;
Dword dwLocaleId;
Dword dwLanguageId;
Char  strCountryAbbrv[4];
Char  strCountry[10];
} Packet;


Packet.Header            = 0xFF;
Packet.MessageId         = 0x50;
Packet.wLength           = sizeof(SID_AUTH_INFO);
Packet.dwProtocolId      = 0x0;
Packet.dwPlatformId      = '68XI';
Packet.dwProductId       = 'RATS';
Packet.dwVersion         = 0xCD;
Packet.dwProductLanguage = 0;
Packet.dwLocalIp         = inet_addr("192.168.0.2");
Packet.dwTimeZoneBias    = 600;
Packet.dwLocaleId        = 0;
Packet.dwLanguageId      = (Dword) GetUserDefaultLangID();
strcpy(Packet.strCountryAbbrv, "Aus");
strcpy(Packet.strCountry, "Australia");


if(InitBnetSocket())
{
std::cout << "Created Battle.net socket" << std::endl;

if(ConnectToBnet())
{
std::cout << "Connected to Battle.net" << std::endl;

Dword Protocol = 0x01;

std::cout << "Sent: " << send(hBnetSocket, (const char*) "\x1", 1, NULL) << std::endl;
std::cout << "Sent: " << send(hBnetSocket, (const char*) &Packet, sizeof(SID_AUTH_INFO), NULL) << std::endl;

ProcessIncommingData(NULL);


closesocket(hBnetSocket);
}

else std::cout << "Failed to connect to Battle.net" << std::endl;
}

else std::cout << "Failed to create Battle.net socket.";

WSACleanup();


char x;
std::cin >> x;
}



And the code as a whole if it's relevent;
BnetDiag.h

/*
* BnetDiag.h
* ==========
* Copyright (C) Matthew J. W.
* All rights reserved.
* Version 1.0
*
*
* Description:
*   Master include file for the "Battle.net Diagnostic Tool"
*   project.
*
*
************************************************************************************************************************/
#define _WIN32_WINNT  0x0400



//----------------------------------------------------------
// Marcos
//----------------------------------------------------------
#define SOCKET_BUFFER_SIZE  512


//----------------------------------------------------------
// System Includes
//----------------------------------------------------------
#include <winsock2.h> // Windows Sockets API.
#include <windows.h>  // Windows API.


//----------------------------------------------------------
// Fundamental Types
//----------------------------------------------------------
typedef void               Void;
typedef char    Char;
typedef unsigned long      Bool;
typedef unsigned char      Byte;
typedef unsigned short     Word;
typedef unsigned long      Dword;
typedef unsigned long long Qword;
typedef signed short       Short;
typedef signed long        Int;
typedef signed long long   Long;


//----------------------------------------------------------
// Function Prototypes
//----------------------------------------------------------
extern Dword __stdcall ProcessIncommingData(Void* p);
extern Bool __stdcall  InitBnetSocket();
extern Bool __stdcall  ConnectToBnet();
extern Bool __stdcall  InitBnlsSocket();
extern Bool __stdcall  ConnectToBnls();



BnetDiag.cpp

/*
* BnetDiag.cpp
* ============
* Copyright (C) Matthew J. W.
* All rights reserved.
* Version 1.0
*
*
* Description:
*   Contains all the functionality of the "Battle.net Diagnostic Tool"
*   project.
*
*
************************************************************************************************************************/
#include "BnetDiag.h"
#include <iostream>



////////////////////////////////////////////////////////////
// Sockets
////////////////////////////////////////////////////////////
SOCKET hBnetSocket; // Handle to the socket used for interacting with Battle.net.
SOCKET hBnlsSocket; // Handle to the socket used for interacting with BNLS.


////////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////////
SOCKADDR_IN BnetSockAddress; // Address of which the BnetSocket will be bound to.
SOCKADDR_IN BnlsSockAddress; // Address of which the BnlsSocket will be bound to.
SOCKADDR_IN BnetHostAddress; // Address of the Battle.net server to connect to.
SOCKADDR_IN BnlsHostAddress; // Address of the BNLS server.
Bool        bThreadIsIdle;
Bool        bSuspendThread;
HANDLE      hWorkerThread;
Byte        SocketBuffer[SOCKET_BUFFER_SIZE];



////////////////////////////////////////////////////////////
// Application Entry Point
////////////////////////////////////////////////////////////

void main()
{
bThreadIsIdle = FALSE;
bSuspendThread = FALSE;


WSAData WSI;
ZeroMemory(&BnetSockAddress, sizeof(SOCKADDR_IN));
ZeroMemory(&BnetHostAddress, sizeof(SOCKADDR_IN));

BnetSockAddress.sin_family = AF_INET;

BnetHostAddress.sin_family = AF_INET;
BnetHostAddress.sin_addr.s_addr = inet_addr("63.240.202.130");
BnetHostAddress.sin_port = htons(6112);

WSAStartup(MAKEWORD(2, 2), &WSI);


struct SID_AUTH_INFO
{
Byte  Header;
Byte  MessageId;
Word  wLength;
Dword dwProtocolId;
Dword dwPlatformId;
Dword dwProductId;
Dword dwVersion;
Dword dwProductLanguage;
Dword dwLocalIp;
Dword dwTimeZoneBias;
Dword dwLocaleId;
Dword dwLanguageId;
Char  strCountryAbbrv[4];
Char  strCountry[10];
} Packet;


Packet.Header            = 0xFF;
Packet.MessageId         = 0x50;
Packet.wLength           = sizeof(SID_AUTH_INFO);
Packet.dwProtocolId      = 0x0;
Packet.dwPlatformId      = '68XI';
Packet.dwProductId       = 'RATS';
Packet.dwVersion         = 0xCD;
Packet.dwProductLanguage = 0;
Packet.dwLocalIp         = inet_addr("192.168.0.2");
Packet.dwTimeZoneBias    = 600;
Packet.dwLocaleId        = 0;
Packet.dwLanguageId      = (Dword) GetUserDefaultLangID();
strcpy(Packet.strCountryAbbrv, "Aus");
strcpy(Packet.strCountry, "Australia");


if(InitBnetSocket())
{
std::cout << "Created Battle.net socket" << std::endl;

if(ConnectToBnet())
{
std::cout << "Connected to Battle.net" << std::endl;

Dword Protocol = 0x01;

std::cout << "Sent: " << send(hBnetSocket, (const char*) &Protocol, sizeof(Dword), NULL) << std::endl;
std::cout << "Sent: " << send(hBnetSocket, (const char*) &Packet, sizeof(SID_AUTH_INFO), NULL) << std::endl;

ProcessIncommingData(NULL);


closesocket(hBnetSocket);
}

else std::cout << "Failed to connect to Battle.net" << std::endl;
}

else std::cout << "Failed to create Battle.net socket.";

WSACleanup();


char x;
std::cin >> x;
}



////////////////////////////////////////////////////////////
// Socket Incomming-Data Processing
////////////////////////////////////////////////////////////

//----------------------------------------------------------
// Dword ProcessIncommingData(Void*)
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
// Description:
//   Manages data which is sent from the Bnet and Bnls socket's
//   peers.
//
//
// Parameters:
//   [Void*] [p] - Unused.
//
//
// Return:
//   Returns TRUE upon success, elsewise FALSE is returned.
//
//
Dword __stdcall ProcessIncommingData(Void* p)
{
fd_set  BnetSockStatus;
fd_set  BnlsSockStatus;
TIMEVAL Timeout;


//
// Initialize socket status buffers.
//
*BnetSockStatus.fd_array = hBnetSocket;
*BnlsSockStatus.fd_array = hBnlsSocket;


//
// Setup the required timeout construct for use during
// status testing.
//
Timeout.tv_sec  = 0;
Timeout.tv_usec = 0;


//
// Continuously flush the receive buffer of both the Bnet
// and BNLS sockets, and process the received data appropiately.
//
for(;;)
{
//
// Check if we have been asked to suspend execution.
//
if(bSuspendThread)
{
//
// Adjust the appropiate flags.
//
bThreadIsIdle  = TRUE;
bSuspendThread = FALSE;


//
// Suspend the worker thread.
//
SuspendThread(hWorkerThread);


//
// Update socket handles in their associated status buffers,
// incase they've been modified during our suspension.
//
*BnetSockStatus.fd_array = hBnetSocket;
*BnlsSockStatus.fd_array = hBnlsSocket;
}


//
// Reset the BnetSocket's status buffer element count.
//
BnetSockStatus.fd_count = 1;


//
// Test the readability of the BnetSocket.
//
if(select(NULL, &BnetSockStatus, NULL, NULL, (const TIMEVAL*) &Timeout) == 1)
{
//
// Debugging.
//
ZeroMemory(SocketBuffer, 512);

Int iBytes = recv(hBnetSocket, (char*) SocketBuffer, 512, NULL);

std::cout << "Received '" << iBytes << "'." << std::endl;
std::cout << SocketBuffer << std::endl << std::endl;

if(iBytes == 0 || iBytes == -1)
{
std::cout << WSAGetLastError();
break;
}
}


//
// Reset the BnlsSocket's status buffer element count.
//
BnlsSockStatus.fd_count = 1;


//
// Test the readability of the BnlsSocket.
//
if(select(NULL, &BnlsSockStatus, NULL, NULL, (const TIMEVAL*) &Timeout) == 1)
{
//
// Readable.
//
}


//
// Yield execution to the next ready thread, or if that's
// not possible just timeout the current thread for a
// dummy millisecond.
//
if(!SwitchToThread()) Sleep(1);
}


//
// OPERATION FAILURE:
// This point in code should never be reached.
//
return FALSE;
}





////////////////////////////////////////////////////////////
// Battle.net Functions
////////////////////////////////////////////////////////////

//----------------------------------------------------------
// Bool InitBnetSocket()
// =-=-=-=-=-=-=-=-=-=-=-
//
// Description:
//   Creates the BnetSocket, binds it to it's correct address,
//   and places it in asynchronous (non-blocking) mode.
//
//
// Parameters:
//   None.
//
//
// Return:
//   Returns TRUE upon success, elsewise FALSE is returned.
//
//
Bool __stdcall InitBnetSocket()
{
//
// Create a new Window's Socket, the BnetSocket.
//
hBnetSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


//
// Ensure the BnetSocket was created successfully before continuing.
//
if(hBnetSocket != INVALID_SOCKET)
{
//
// Bind the BnetSocket to it's predefined address.
//
if(!bind(hBnetSocket, (const SOCKADDR*) &BnetSockAddress, sizeof(SOCKADDR_IN)))
{
Dword dwAsync = TRUE;


//
// Place the BnetSocket in asynchronous (non-blocking) mode.
//
if(!ioctlsocket(hBnetSocket, FIONBIO, (u_long*) &dwAsync))
{
//
// OPERATION SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
}
}

else
{
//
// ERROR:
//
}


//
// Close the BnetSocket.
//
closesocket(hBnetSocket);
}

else
{
//
// ERROR:
//
}


//
// OPERATION FAILURE:
//
return FALSE;
}


//----------------------------------------------------------
// Bool ConnectToBnet()
// =-=-=-=-=-=-=-=-=-=-=-
//
// Description:
//   Connects the BnetSocket to the Battle.net server.
//
//
// Parameters:
//   None.
//
//
// Return:
//   Returns TRUE upon success, elsewise FALSE is returned.
//
//
// Remarks:
//   ConnectToBnet functions like a blocking call, and does not
//   return until the final outcome of the connection-attempt
//   is discovered.
//
//
Bool __stdcall ConnectToBnet()
{
//
// Initiate a connection with Battle.net.
//
connect(hBnetSocket, (const SOCKADDR*) &BnetHostAddress, sizeof(SOCKADDR_IN));


//
// Ensure that a connection with Battle.net was intiated
// successfully.
//
// Due to a connection can never be established immediately
// when using non-blocking sockets (As we are); 'connect'
// will always fail with WSAEWOULDBLOCK, this is expected,
// but any other error is a _real_ error.
//
if(WSAGetLastError() == WSAEWOULDBLOCK)
{
fd_set  Status;
TIMEVAL Timeout;


//
// Setup the status construct for use in testing the status
// of the BnetSocket.
//
*Status.fd_array = hBnetSocket;


//
// Setup the required timeout construct for use during
// status testing.
//
Timeout.tv_sec  = 0;
Timeout.tv_usec = 0;


//
// Wait to see the outcome of the connection-attempt.
//
for(;;)
{
//
// Reset status element count.
//
Status.fd_count = 1;


//
// Test the BnetSocket for writeability.
//
if(select(NULL, NULL, &Status, NULL, (const TIMEVAL*) &Timeout) == 1)
{
//
// OPERATION SUCCESS:
// A connection has been established with Battle.net.
//
return TRUE;
}


//
// Reset status element count.
//
Status.fd_count = 1;


//
// Test the BnetSocket for errors.
//
if(select(NULL, NULL, NULL, &Status, (const TIMEVAL*) &Timeout) == 1)
{
//
// ERROR:
//


//
// We have failed to connect to Battle.net.
//
break;
}


//
// Yield execution to the next ready thread, or if that's
// not possible just timeout the current thread for a
// dummy millisecond.
//
if(!SwitchToThread()) Sleep(1);
}
}

else
{
//
// ERROR:
//
}


//
// OPERATION FAILURE:
//
return FALSE;
}



////////////////////////////////////////////////////////////
// BNLS Functions
////////////////////////////////////////////////////////////

//----------------------------------------------------------
// Bool InitBnlsSocket()
// =-=-=-=-=-=-=-=-=-=-=-
//
// Description:
//   Creates the BnlsSocket, binds it to it's correct address,
//   and places it in asynchronous (non-blocking) mode.
//
//
// Parameters:
//   None.
//
//
// Return:
//   Returns TRUE upon success, elsewise FALSE is returned.
//
//
Bool __stdcall InitBnlsSocket()
{
//
// Create a new Window's Socket, the BnlsSocket.
//
hBnlsSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


//
// Ensure the BnlsSocket was created successfully before continuing.
//
if(hBnlsSocket != INVALID_SOCKET)
{
//
// Bind the BnlsSocket to it's predefined address.
//
if(!bind(hBnlsSocket, (const SOCKADDR*) &BnlsSockAddress, sizeof(SOCKADDR_IN)))
{
Dword dwAsync = TRUE;


//
// Place the BnlsSocket in asynchronous (non-blocking) mode.
//
if(!ioctlsocket(hBnlsSocket, FIONBIO, (u_long*) &dwAsync))
{
//
// OPERATION SUCCESS:
//
return TRUE;
}

else
{
//
// ERROR:
//
}
}

else
{
//
// ERROR:
//
}


//
// Close the BnlsSocket.
//
closesocket(hBnlsSocket);
}

else
{
//
// ERROR:
//
}


//
// OPERATION FAILURE:
//
return FALSE;
}


//----------------------------------------------------------
// Bool ConnectToBnls()
// =-=-=-=-=-=-=-=-=-=-=-
//
// Description:
//   Connects the BnlsSocket to the Battle.net server.
//
//
// Parameters:
//   None.
//
//
// Return:
//   Returns TRUE upon success, elsewise FALSE is returned.
//
//
// Remarks:
//   ConnectToBnls functions like a blocking call, and does not
//   return until the final outcome of the connection-attempt
//   is discovered.
//
//
Bool __stdcall ConnectToBnls()
{
//
// Initiate a connection with Battle.net.
//
connect(hBnlsSocket, (const SOCKADDR*) &BnlsHostAddress, sizeof(SOCKADDR_IN));


//
// Ensure that a connection with Battle.net was intiated
// successfully.
//
// Due to a connection can never be established immediately
// when using non-blocking sockets (As we are); 'connect'
// will always fail with WSAEWOULDBLOCK, this is expected,
// but any other error is a _real_ error.
//
if(WSAGetLastError() == WSAEWOULDBLOCK)
{
fd_set  Status;
TIMEVAL Timeout;


//
// Setup the status construct for use in testing the status
// of the BnlsSocket.
//
*Status.fd_array = hBnlsSocket;


//
// Setup the required timeout construct for use during
// status testing.
//
Timeout.tv_sec  = 0;
Timeout.tv_usec = 0;


//
// Wait to see the outcome of the connection-attempt.
//
for(;;)
{
//
// Reset status element count.
//
Status.fd_count = 1;


//
// Test the BnlsSocket for writeability.
//
if(select(NULL, NULL, &Status, NULL, (const TIMEVAL*) &Timeout) == 1)
{
//
// OPERATION SUCCESS:
// A connection has been established with Battle.net.
//
return TRUE;
}


//
// Reset status element count.
//
Status.fd_count = 1;


//
// Test the BnlsSocket for errors.
//
if(select(NULL, NULL, NULL, &Status, (const TIMEVAL*) &Timeout) == 1)
{
//
// ERROR:
//


//
// We have failed to connect to Battle.net.
//
break;
}


//
// Yield execution to the next ready thread, or if that's
// not possible just timeout the current thread for a
// dummy millisecond.
//
if(!SwitchToThread()) Sleep(1);
}
}

else
{
//
// ERROR:
//
}


//
// OPERATION FAILURE:
//
return FALSE;
}

Kp

Yes, it's a protocol selector.  Useful values are 1 (bncs logon), 2 (bnftp).  3 used to select the chat gateway, but that's been shutdown, so it just sends you a short message and kicks you off.  Some other values have been discovered which don't cause an immediate disconnect, but their purpose is unknown.  There's much speculation about their meaning.

Your definition looks right, but if you're going to be sending packed structures (which gets difficult when you get farther into the protocol and start using multiple variable-length fields, e.g. profile commands), it'd be cleaner to do:struct BNCSHeader {
Byte Header;
Byte MessageId;
Word wLength;
};

struct SID_AUTH_INFO : public BNCSHeader {
/* all the other fields from your struct... */
};


The last field is variable length, and holds the full name of your current country.

Note that multi-character constants ('68XI', 'RATS') have a compiler dependent behavior and should be avoided.  Just because Blizzard used them does not mean you should too.

What four bytes do you receive?  I'm guessing it's ff 00 04 00, which is a generic connection probe command that you can safely ignore.

Unless you're targetting Windows 98 systems, it's probably OK to define _WIN32_WINNT to 0x500, so that you can access Windows 2000 features.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Win32

Quote
Yes, it's a protocol selector.  Useful values are 1 (bncs logon), 2 (bnftp).  3 used to select the chat gateway, but that's been shutdown, so it just sends you a short message and kicks you off.  Some other values have been discovered which don't cause an immediate disconnect, but their purpose is unknown.  There's much speculation about their meaning.
Ah, cool, thanks for clearing that up.

Quote
Your definition looks right, but if you're going to be sending packed structures (which gets difficult when you get farther into the protocol and start using multiple variable-length fields, e.g. profile commands), it'd be cleaner to do:
I don't actually plan to use packed structures, I'm just using some very primitive techniques during my testing/debugging stage.

Quote
The last field is variable length, and holds the full name of your current country.

Note that multi-character constants ('68XI', 'RATS') have a compiler dependent behavior and should be avoided.  Just because Blizzard used them does not mean you should too.

What four bytes do you receive?  I'm guessing it's ff 00 04 00, which is a generic connection probe command that you can safely ignore.

Unless you're targetting Windows 98 systems, it's probably OK to define _WIN32_WINNT to 0x500, so that you can access Windows 2000 features.
Does this mean the country abbreviation field is not interpreted by the BNCS as a null-terminated string? And is simply a static 3 characters?

And yeah, those are the four bytes.

I'm planning to target any OS based at Windows NT.

Thanks for all your help and patience.

Oh, and is there any reason my simple example code doesn't work? (I get disconencted)


-Matt

Kp

Quote from: Win32 on May 27, 2006, 07:17 PMDoes this mean the country abbreviation field is not interpreted by the BNCS as a null-terminated string? And is simply a static 3 characters?

And yeah, those are the four bytes.

I'm planning to target any OS based at Windows NT.

Strictly speaking, the last two fields are both variable length.  However, AFAIK, all the country abbreviations are 3 letters, so it's OK to specify that as fixed at 3 characters+null.  The server can tolerate a variable length there (or did a few years ago).

Targetting anything based on NT is a nice goal, but NT4 systems are quite rare these days (even Windows 2000 is largely phased out, aside from corporate markets and Windows geeks who prefer it over Windows XP).
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Win32

Quote
Strictly speaking, the last two fields are both variable length.  However, AFAIK, all the country abbreviations are 3 letters, so it's OK to specify that as fixed at 3 characters+null.  The server can tolerate a variable length there (or did a few years ago).

Targetting anything based on NT is a nice goal, but NT4 systems are quite rare these days (even Windows 2000 is largely phased out, aside from corporate markets and Windows geeks who prefer it over Windows XP).
Ah alright, that's cleared some things up, thanks.

Would you happen to have any idea why I get disconnected after sending the protocol selector, then 0x50?
The following pseudo-code shows what I'm doing briefly;


//
// 0x50 message structure.
//
struct SID_AUTH_INFO
{
Byte  Header;
Byte  MessageId;
Word  wLength;

Dword dwProtocolId;
Dword dwPlatformId;
Dword dwProductId;
Dword dwVersion;
Dword dwProductLanguage;
Dword dwLocalIp;
Dword dwTimeZoneBias;
Dword dwLocaleId;
Dword dwLanguageId;
Char  strCountryAbbrv[3];
Char  strCountry[10];
} Packet;


//
// Setup 0x50 construct.
//
ZeroMemory(&Packet, sizeof(SID_AUTH_INFO));

Packet.Header            = 0xFF;
Packet.MessageId         = 0x50;
Packet.wLength           = sizeof(SID_AUTH_INFO);
Packet.dwProtocolId      = 0x0;
Packet.dwPlatformId      = '68XI';
Packet.dwProductId       = 'RATS';
Packet.dwVersion         = 0xCD;
Packet.dwProductLanguage = 0;
Packet.dwLocalIp         = inet_addr("192.168.0.2");
Packet.dwTimeZoneBias    = 600;
Packet.dwLocaleId        = 0;
Packet.dwLanguageId      = (Dword) GetUserDefaultLangID();
strcpy(Packet.strCountryAbbrv, "Aus");
strcpy(Packet.strCountry, "Australia");


//
// Send protocol selector to Battle.net.
//
Dword dwProtocol = 1;
send(sock, (const char*) &dwProtocol, sizeof(Dword), NULL);


//
// Send 0x50
//
send(sock, (const char*) &Packet, sizeof(SID_AUTH_INFO), NULL);


The only response I ever get is a connection-closure, or, if I only send the protocol selector I receive those 4 obsecure bytes after quite some time has elapsed.


-Matt

FrOzeN

This is just a guess, (as I don't know C++) but "Australia" is only 9 characters and you've assigned 10 for it. Could it be sending "Australia" with an extra space making it invalid?
~ FrOzeN

Win32

Quote
This is just a guess, (as I don't know C++) but "Australia" is only 9 characters and you've assigned 10 for it. Could it be sending "Australia" with an extra space making it invalid?
I was under the presumption it needed to be null-terminated, due to the varying lengths of country names.


-Matt

FrOzeN

Yeah it does. I didn't understand you're code. Also, in that case the abbreviation has to be Null Terminated aswell (despite it always being 3 characters).
~ FrOzeN

Win32

Quote
Yeah it does. I didn't understand you're code. Also, in that case the abbreviation has to be Null Terminated aswell (despite it always being 3 characters).
I initially had it as being null-terminated, but currently it does not make any difference in that I'm still being disconnected no matter which way I go.


-Matt

Win32

Hmm, I think I've resolved the problem. VC++ was specified to automatically align constructs.
Thanks for all your help guys.


-Matt

rabbit

Quote from: Kp on May 27, 2006, 09:42 PM
Quote from: Win32 on May 27, 2006, 07:17 PMDoes this mean the country abbreviation field is not interpreted by the BNCS as a null-terminated string? And is simply a static 3 characters?

And yeah, those are the four bytes.

I'm planning to target any OS based at Windows NT.

Strictly speaking, the last two fields are both variable length.  However, AFAIK, all the country abbreviations are 3 letters, so it's OK to specify that as fixed at 3 characters+null.  The server can tolerate a variable length there (or did a few years ago).
Really?  IT, DE, JP, UK, IR..so many are 2, or am I missing something?  I know USA and CAN, but I've seen a lot of others...
Grif: Yeah, and the people in the red states are mad because the people in the blue states are mean to them and want them to pay money for roads and schools instead of cool things like NASCAR and shotguns.  Also, there's something about ketchup in there.

Yegg

Quote from: rabbit on May 28, 2006, 09:09 AM
Quote from: Kp on May 27, 2006, 09:42 PM
Quote from: Win32 on May 27, 2006, 07:17 PMDoes this mean the country abbreviation field is not interpreted by the BNCS as a null-terminated string? And is simply a static 3 characters?

And yeah, those are the four bytes.

I'm planning to target any OS based at Windows NT.

Strictly speaking, the last two fields are both variable length.  However, AFAIK, all the country abbreviations are 3 letters, so it's OK to specify that as fixed at 3 characters+null.  The server can tolerate a variable length there (or did a few years ago).
Really?  IT, DE, JP, UK, IR..so many are 2, or am I missing something?  I know USA and CAN, but I've seen a lot of others...

This isn't the most useful post, but I remember seeing a "SPA" (Spanish). I've never seen one with two characters.

UserLoser

Quote from: Win32 on May 27, 2006, 10:30 PM
Quote
Strictly speaking, the last two fields are both variable length.  However, AFAIK, all the country abbreviations are 3 letters, so it's OK to specify that as fixed at 3 characters+null.  The server can tolerate a variable length there (or did a few years ago).

Targetting anything based on NT is a nice goal, but NT4 systems are quite rare these days (even Windows 2000 is largely phased out, aside from corporate markets and Windows geeks who prefer it over Windows XP).
Ah alright, that's cleared some things up, thanks.

Would you happen to have any idea why I get disconnected after sending the protocol selector, then 0x50?
The following pseudo-code shows what I'm doing briefly;


//
// 0x50 message structure.
//
struct SID_AUTH_INFO
{
Byte  Header;
Byte  MessageId;
Word  wLength;

Dword dwProtocolId;
Dword dwPlatformId;
Dword dwProductId;
Dword dwVersion;
Dword dwProductLanguage;
Dword dwLocalIp;
Dword dwTimeZoneBias;
Dword dwLocaleId;
Dword dwLanguageId;
Char  strCountryAbbrv[3];
Char  strCountry[10];
} Packet;


//
// Setup 0x50 construct.
//
ZeroMemory(&Packet, sizeof(SID_AUTH_INFO));

Packet.Header            = 0xFF;
Packet.MessageId         = 0x50;
Packet.wLength           = sizeof(SID_AUTH_INFO);
Packet.dwProtocolId      = 0x0;
Packet.dwPlatformId      = '68XI';
Packet.dwProductId       = 'RATS';
Packet.dwVersion         = 0xCD;
Packet.dwProductLanguage = 0;
Packet.dwLocalIp         = inet_addr("192.168.0.2");
Packet.dwTimeZoneBias    = 600;
Packet.dwLocaleId        = 0;
Packet.dwLanguageId      = (Dword) GetUserDefaultLangID();
strcpy(Packet.strCountryAbbrv, "Aus");
strcpy(Packet.strCountry, "Australia");


//
// Send protocol selector to Battle.net.
//
Dword dwProtocol = 1;
send(sock, (const char*) &dwProtocol, sizeof(Dword), NULL);


//
// Send 0x50
//
send(sock, (const char*) &Packet, sizeof(SID_AUTH_INFO), NULL);


The only response I ever get is a connection-closure, or, if I only send the protocol selector I receive those 4 obsecure bytes after quite some time has elapsed.


-Matt


strcpy(Packet.strCountryAbbrv, "Aus\0");
strcpy(Packet.strCountry, "Australia\0");

Byte Protocol = 1;
send(sock, (const char*)Protocol, 1, 0);


Try that, also supply packet capture.  Who knows what exacty sizeof is returning.  All I know, it's most likely a bad idea using sizeof to specify length when there are variable length strings in the message

FrOzeN

Quote from: rabbit on May 28, 2006, 09:09 AM
Quote from: Kp on May 27, 2006, 09:42 PM
Quote from: Win32 on May 27, 2006, 07:17 PMDoes this mean the country abbreviation field is not interpreted by the BNCS as a null-terminated string? And is simply a static 3 characters?

And yeah, those are the four bytes.

I'm planning to target any OS based at Windows NT.

Strictly speaking, the last two fields are both variable length.  However, AFAIK, all the country abbreviations are 3 letters, so it's OK to specify that as fixed at 3 characters+null.  The server can tolerate a variable length there (or did a few years ago).
Really?  IT, DE, JP, UK, IR..so many are 2, or am I missing something?  I know USA and CAN, but I've seen a lot of others...
All countries have codes representing them in both 2 and 3 letter abbreviations. Though, Battle.net requests the 3 character country code. Eg, you said "JP"; Battle.net wants "JPN".

Preferably, it's best just to collect the information with the GetLocaleInfo() API.
~ FrOzeN

Joe[x86]

Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.