• Welcome to Valhalla Legends Archive.
 

C++ Basic Connection

Started by Trunning, March 27, 2010, 03:25 AM

Previous topic - Next topic

Trunning

I don't play on useast.
Compiler won't let use '\x01', I have to type cast it.
I found out the error, and fixed it.
The client can't figure out where I live.
Can't help it.

rabbit

Hdx said "\x1" which is different from '\x01'.
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.

Trunning

Ah, read that wrong. Either way, compiler still complains.

Zoo

Use quotes not apostrophes, works a lot better.

Joe[x86]

They're called double-quotes and single-quotes in the professional environment.
Quote from: brew on April 25, 2007, 07:33 PM
that made me feel like a total idiot. this entire thing was useless.

Yegg

#35
Omg. I almost shit myself when you went as far as to take a screenshot of your using Wireshark. Maybe if you told us packet log results from the start your issue would be solved by now? Because here is what you need to do:

First, change the below line:
sent = send(sockBNCS, (const char*)'\x01', 1, NULL);
to
sent = send(sockBNCS, "\x01", 1, NULL);

When setting pkt.LangID to the value returned by GetUserDefaultLangID(), the type cast to DWORD is not necesssary. GetUserDefaultLangID() returns a WORD (which is listed as a LANGID type in this case).

I made a change to your SID_AUTH_INFO struct.

struct SID_AUTH_INFO {
BNCS_HEADER Header;
DWORD   ProtocolID;
DWORD   PlatformID;
DWORD   ProductID;
DWORD   VerByte;
DWORD   ProductLang;
DWORD   LocalIP;
DWORD   TimeZone;
DWORD   LocaleID;
DWORD   LangID;
char *CountryAbbr;
char *CountryName;
};


Is the new version of it. I got rid of your char CountryInfo[0x100] and the reasoning will be explained below.


Many major changes took place with the below code of yours:
 strcpy(&pkt.CountryInfo[0], "AUS"); //I *think* strcpy() returns the number of bytes copied, can't remember
 AbbrLen = sizeof(pkt.CountryInfo[0]);
 strcpy(&pkt.CountryInfo[AbbrLen + 1], "Australia");
 NameLen = sizeof(pkt.CountryInfo[AbbrLen + 1]);
 
 pkt.Header.Sanity = 0xFF;
 pkt.Header.ID     = 0x50;
 pkt.Header.Length =  4       //Header Length
                    + (4 * 9) //Number of DWORDs in the packet
                    + AbbrLen //Abbreviation Length
                    + 1       //Null Terminator
                    + NameLen //Country Name Length
                    + 1;      //Null Terminator
 printf("Sending %d bytes for 0x50 SID_AUTH_INFO\n", pkt.Header.Length);
 sent = send(sockBNCS, (const char*)&pkt, pkt.Header.Length, NULL);


I changed it to the code below (which I'll explain):
 pkt.CountryAbbr = (char *) malloc (4);
 pkt.CountryName = (char *) malloc (10);
 strcpy (pkt.CountryAbbr, "AUS");
 strcpy (pkt.CountryName, "Australia");

 int pkt_size = sizeof(pkt) - 8;
 
 pkt.Header.Sanity = 0xFF;
 pkt.Header.ID     = 0x50;
 pkt.Header.Length = pkt_size + 14; // 14 being strlen of CountryAbbr + CountryName inc. nulls.
 char *buffer;
 
 buffer = (char *) malloc (pkt.Header.Length);
 memcpy (buffer, &pkt, pkt_size);
 memcpy (buffer+pkt_size, pkt.CountryAbbr, 4);
 memcpy (buffer+pkt_size+4, pkt.CountryName, 10);

 printf("Sending %d bytes for 0x50 SID_AUTH_INFO\n", pkt.Header.Length);
 sent = send(sockBNCS, (const char*)buffer, pkt.Header.Length, NULL);


I'm not sure why you wanted to separate the country abbreviation and name into a single char array, but its better to store them in their own area in memory. It also looks neater.

The reason I instead have 2 char pointers is so that we can define the size of each variable depending on how much space we need. Sure, in your case you hardcoded your country info in. But if you were making an actual application you planned to release (or even for just yourself, for the sake of better design), you would probably want to make a call to GetLocaleInfo to retrieve the country abbreviation and name. GetLocaleInfo() is pretty simple (and is found on MSDN):
len = GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SENGCOUNTRY, NULL, 0);
SID_AUTH_INFO.CountryName = (char*) malloc (len);
GetLocaleInfo (LOCALE_USER_DEFAULT,LOCALE_SENGCOUNTRY, (LPSTR)SID_AUTH_INFO.CountryName, len);

On the first call to GetLocaleInfo, it returns the length of the string that represents the info you are seeking. On the second call, it stores that string in a buffer variable. And as you can see, in that example code, I allocate the proper amount of memory to CountryName, and I use it with GetLocaleInfo.

In your code, I tried keeping it as close to the code you already had, without changing else (because let's face it.. there is a tremendous amount of room for improvement). In case you didn't figure it out, I allocated 4 bytes to pkt.CountryAbbr (even though "AUS" is 3 letters) because the 4th byte is how the null terminator gets accounted for (same applies for pkt.CountryName of course).

The meaning behind sizeof(pkt) - 8 is because when we obtain the size of the entire struct, the 2 char pointers (remember: pointer gets treated as unsigned long -- so 4 bytes) dont have a defined size in the way we know them to be. the struct is just aware that those 2 members are pointers (thus, 4 bytes each). So this is why we subtract 8 when doing sizeof(pkt).

When you calculated your value for pkt.Header.Length, you had quite a mess there. As you can see,
pkt.Header.Length = pkt_size + 14
looks much nicer.
Since I think it'd be nice if you incorporated GetLocaleInfo into your code, I didnt worry about doing a strlen() on either CountryAbbr or CountryName and just hardcoded the total length in. This way.. if you decide to use GetLocaleInfo (do it), you'll be able to replace that 14 accordingly.

I always find it nicer to just use some kind of buffer variable to store your data in prior to writing it to a file or sending over a socket (or whatever other technology you choose). So I did just that. I just tossed this together
 memcpy (buffer+pkt_size, pkt.CountryAbbr, 4);
 memcpy (buffer+pkt_size+4, pkt.CountryName, 10);

So in reality, since we may not always know the length of CountryAbbr or CountryName, we wouldn't be able to just hardcode it in like that. I have it this way for examples sake and to show you what needs to be done to make your code work nicely.

And don't forget the last line...
sent = send(sockBNCS, (const char*)buffer, pkt.Header.Length, NULL);
only change here is that &pkt is replaced with buffer.

Again, this was tossed together so that it would work correctly, and for examples sake. Of course you'll want to free up any unused memory before you're done (CountryAbbr, CountryName, and buffer need to be freed (ie. use free() function in case you're unaware)).

If you need any more help.. just ask.

Zoo

Quote from: Joex86] link=topic=18199.msg184457#msg184457 date=1270200478]
They're called double-quotes and single-quotes in the professional environment.

Noted, but I have a feeling he understands the difference better when the explanation sets them as far from each other as possible :)

Hdx

Yegg, I made it a char[] instead of pointers for a reason, he's not smart enough to copy structs.
I chose 0x100 at random just because I have never seen any country name + abbr come any where near that length, so it's fairly easy.
Though, you can do it either way. Because IIRC, NO packet in the bnet protocol has a string followed by anything else but a string.

Anyways, the code as it stands is pretty much complete crap anyways, and unless he is willing to take advice and actually learn, all of this is moot.

Proud host of the JBLS server www.JBLS.org.
JBLS.org Status:
JBLS/BNLS Server Status

Yegg

Quote from: Hdx on April 02, 2010, 01:49 PM
Yegg, I made it a char[] instead of pointers for a reason, he's not smart enough to copy structs.
I chose 0x100 at random just because I have never seen any country name + abbr come any where near that length, so it's fairly easy.
Though, you can do it either way. Because IIRC, NO packet in the bnet protocol has a string followed by anything else but a string.

Anyways, the code as it stands is pretty much complete crap anyways, and unless he is willing to take advice and actually learn, all of this is moot.

Ah, I thought that was something he came up with in regards to the char[]. Either way, I figured I'd try to explain thoroughly and give examples, so hopefully he'll at least learn something.

And yes, I agree, the code is pretty bad. Aside from it being fine for merely testing a connection to battle.net, if he plans to use this code in an actual app, it could definitely use a huge makeover.

Trunning

#39
Ok I completely just re-made this based on HDX's code, and then implemented Yegg's. I also decided just to use usa instead.

In my packet dump in WS it doesn't appear to be including D2DV. Here is my current code.

I'm pretty sure unless I send the right information, Battle.net won't send back either 0x25 or 0x50.
#pragma comment (lib, "Ws2_32.lib")
#include <windows.h>
#include <winsock.h>
#include <string>
#include <iostream>
using namespace std;

struct BNCS_HEADER {
BYTE bHead;
BYTE bID;
WORD wLen;
};

struct SID_AUTH_INFO {
BNCS_HEADER Header;
DWORD   ProtocolID;
DWORD   PlatformID;
DWORD   ProductID;
DWORD   VerByte;
DWORD   ProductLang;
DWORD   LocalIP;
DWORD   TimeZone;
DWORD   LocaleID;
DWORD   LangID;
char *CountryAbbr;
char *CountryName;
};

int socket_connect(SOCKET sockBNCS, const char *Server, WORD Port){
LPHOSTENT host = gethostbyname(Server);
if (!host)
return -1;

SOCKADDR_IN info;
info.sin_family = AF_INET;
info.sin_addr = *((LPIN_ADDR)*host->h_addr_list);
info.sin_port = htons(Port);

if (connect(sockBNCS, (LPSOCKADDR)&info, sizeof(info)) == SOCKET_ERROR){
return -1;
}

return 0;
}

int send_SID_AUTH_INFO(SOCKET sockBNCS){
SID_AUTH_INFO pkt;
int sent;
memset(&pkt, 0, sizeof(SID_AUTH_INFO));

pkt.ProtocolID = 0x00;
pkt.PlatformID = 'IX86';
pkt.ProductID = 'D2DV';
pkt.VerByte = 0x00;
pkt.ProductID = 0x00;
pkt.LocalIP = inet_addr("192.168.1.100");
pkt.TimeZone = 600;
pkt.LocaleID = 0x00;
pkt.LangID = GetUserDefaultLangID();

pkt.CountryAbbr = (char*)malloc(4);
pkt.CountryName = (char*)malloc(14);
strcpy(pkt.CountryAbbr, "USA");
strcpy(pkt.CountryName, "United States");

int pkt_size = sizeof(pkt) - 8;

pkt.Header.bHead = 0xFF;
pkt.Header.bID = 0x50;
pkt.Header.wLen = pkt_size + 18;

char *buffer;

buffer = (char*)malloc(pkt.Header.wLen);
memcpy(buffer, &pkt, pkt_size);
memcpy(buffer + pkt_size, pkt.CountryAbbr, 4);
memcpy(buffer + pkt_size + 4, pkt.CountryName, 14);

sent = send(sockBNCS, (const char*)buffer, pkt.Header.wLen, NULL);
return sent;
}

int main(){
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData);


SOCKET sockBNCS = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockBNCS == INVALID_SOCKET){
cout << "Failed to create socket!";
cin.ignore();
cin.get();
return -1;
}

if (socket_connect(sockBNCS, "asia.battle.net", 6112) == -1){
cout << "Failed to connect!";
cin.ignore();
cin.get();
return -1;
}

cout << "Sending protocol byte...\n";
int sent;
sent = send(sockBNCS, "\x01", 1, NULL);

if (sent > 0)
cout << "Sent < " << sent << " bytes >\n";
else
cout << "Failed to send protocol byte!\n";

cout << "Sending 0x50...\n";
sent = send_SID_AUTH_INFO(sockBNCS);
cout << "Sent < " << sent << " bytes >\n";

BNCS_HEADER header;
void *data = malloc(0x200);
int data_max = 0x200;

while (true){
sent = recv(sockBNCS, (char*)&header, sizeof(header), NULL);
if (sent != sizeof(header)){
cout << "Didn't receive full header\n";
cin.ignore();
cin.get();
WSACleanup();
return -1;
} else
break;
}

if (header.wLen - 4 > data_max){
free(data);
data = malloc(header.wLen - 4);
}

sent = recv(sockBNCS, (char*)&data, header.wLen - 4, NULL);
if (sent != header.wLen - 4){
cout << "Could not read packet data\n";
cin.ignore();
cin.get();
WSACleanup();
return -1;
}

cout << "Header Id : " << header.bID;

cin.ignore();
cin.get();
closesocket(sockBNCS);
WSACleanup();
return 0;
}


And here my dump.
0000  00 04 ed 6f a5 60 00 26  18 7f 24 a2 08 00 45 00   ...o.`.& ..$...E.
0010  00 62 63 1a 40 00 80 06  01 54 c0 a8 01 65 d3 e9   .bc.@... .T...e..
0020  00 31 09 dd 17 e0 b1 7c  e6 c5 9c 3a 1c 50 50 18   .1.....| ...:.PP.
0030  ff ff 96 7c 00 00 ff 50  3a 00 00 00 00 00 36 38   ...|...P :.....68
0040  58 49 00 00 00 00 00 00  00 00 00 00 00 00 c0 a8   XI...... ........
0050  01 64 58 02 00 00 00 00  00 00 09 04 00 00 55 53   .dX..... ......US
0060  41 00 55 6e 69 74 65 64  20 53 74 61 74 65 73 00   A.United  States.

l)ragon

pkt.LocaleID = GetUserDefaultLCID();
*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*ˆ¨¯¯¨ˆ*^~·.,l)ragon,.-·~^*ˆ¨¯¯¨ˆ*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*

Trunning

Thanks I'll add that, still don't know why D2DV isn't being sent.

l)ragon

set break points through out the function, and put a watch on "pkt" step through and watch the dta in "pkt" for changes it should give you an idea where your problem is at.
*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*ˆ¨¯¯¨ˆ*^~·.,l)ragon,.-·~^*ˆ¨¯¯¨ˆ*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*

Trunning

It appears the data is being set, and I don't how to set a watch.

l)ragon

Quote from: Trunning on April 27, 2010, 09:25 AM
It appears the data is being set, and I don't how to set a watch.
Don't have it infront of me but should be in view, to set the watch highlight and drag onto the watch window, you can watch 'most' variables in that window. (asuming your using MS version btw)
*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*ˆ¨¯¯¨ˆ*^~·.,l)ragon,.-·~^*ˆ¨¯¯¨ˆ*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*

|