1 /*
2  * Copyright (C) 2000,2001	Onlyer	(onlyer@263.net)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18 #include "common/setup_before.h"
19 
20 #include <stddef.h>
21 #include <stdlib.h>
22 #ifdef HAVE_STRING_H
23 # include <string.h>
24 #else
25 # ifdef HAVE_STRINGS_H
26 #  include <strings.h>
27 # endif
28 #endif
29 #include "compat/strcasecmp.h"
30 
31 #include "connection.h"
32 #include "realm.h"
33 #include "account.h"
34 #include "account_wrap.h"
35 #include "game.h"
36 #include "d2cs/d2cs_bnetd_protocol.h"
37 #include "common/bnethash.h"
38 #include "common/bnethashconv.h"
39 #include "common/eventlog.h"
40 #include "common/queue.h"
41 #include "common/packet.h"
42 #include "common/addr.h"
43 #include "common/bn_type.h"
44 #include "prefs.h"
45 #include "common/util.h"
46 #include "common/field_sizes.h"
47 #include "handle_d2cs.h"
48 #include "common/tag.h"
49 #include "common/xalloc.h"
50 #include "common/setup_after.h"
51 
52 static int on_d2cs_accountloginreq(t_connection * c, t_packet const * packet);
53 static int on_d2cs_charloginreq(t_connection * c, t_packet const * packet);
54 static int on_d2cs_authreply(t_connection * c, t_packet const * packet);
55 static int on_d2cs_gameinforeply(t_connection * c, t_packet const * packet);
56 
handle_d2cs_packet(t_connection * c,t_packet const * packet)57 extern int handle_d2cs_packet(t_connection * c, t_packet const * packet)
58 {
59 	if (!c) {
60 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
61 		return -1;
62 	}
63 	if (!packet) {
64 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL packet");
65 		return -1;
66 	}
67 	if (packet_get_class(packet)!=packet_class_d2cs_bnetd) {
68 		eventlog(eventlog_level_error,__FUNCTION__,"got bad packet class %d",
69 		packet_get_class(packet));
70 		return -1;
71 	}
72 	switch (conn_get_state(c)) {
73 		case conn_state_connected:
74 			switch (packet_get_type(packet)) {
75 				case D2CS_BNETD_AUTHREPLY:
76 					on_d2cs_authreply(c,packet);
77 					break;
78 				default:
79 					eventlog(eventlog_level_error,__FUNCTION__,
80 					"got unknown packet type %d",packet_get_type(packet));
81 					break;
82 			}
83 			break;
84 		case conn_state_loggedin:
85 			switch (packet_get_type(packet)) {
86 				case D2CS_BNETD_ACCOUNTLOGINREQ:
87 					on_d2cs_accountloginreq(c,packet);
88 					break;
89 				case D2CS_BNETD_CHARLOGINREQ:
90 					on_d2cs_charloginreq(c,packet);
91 					break;
92 				case D2CS_BNETD_GAMEINFOREPLY:
93 					on_d2cs_gameinforeply(c,packet);
94 					break;
95 				default:
96 					eventlog(eventlog_level_error,__FUNCTION__,
97 					"got unknown packet type %d",packet_get_type(packet));
98 					break;
99 			}
100 			break;
101 		default:
102 			eventlog(eventlog_level_error,__FUNCTION__,
103 				"got unknown connection state %d",conn_get_state(c));
104 			break;
105 	}
106 	return 0;
107 }
108 
on_d2cs_authreply(t_connection * c,t_packet const * packet)109 static int on_d2cs_authreply(t_connection * c, t_packet const * packet)
110 {
111 	t_packet	* rpacket;
112 	unsigned int	version;
113 	unsigned int	try_version;
114 	unsigned int	reply;
115 	char const	* realmname;
116 	t_realm		* realm;
117 
118 	if (packet_get_size(packet)<sizeof(t_d2cs_bnetd_authreply)) {
119 		eventlog(eventlog_level_error,__FUNCTION__,"got bad packet size");
120 		return -1;
121 	}
122 	if (!(realmname=packet_get_str_const(packet,sizeof(t_d2cs_bnetd_authreply),REALM_NAME_LEN))) {
123 		eventlog(eventlog_level_error,__FUNCTION__,"got bad realmname");
124 		return -1;
125 	}
126         if (!(realm=realmlist_find_realm(realmname))) {
127 		realm=realmlist_find_realm_by_ip(conn_get_addr(c)); /* should not fail - checked in handle_init_packet() handle_init.c */
128 		eventlog(eventlog_level_warn,__FUNCTION__, "warn: realm name mismatch %s %s", realm_get_name(realm), realmname);
129 		if (!(prefs_allow_d2cs_setname())) { /* fail if allow_d2cs_setname = false */
130 			eventlog(eventlog_level_error,__FUNCTION__, "d2cs not allowed to set realm name");
131 			return -1;
132 		}
133 		if (realm_get_active(realm)) { /* fail if realm already active */
134 			eventlog(eventlog_level_error,__FUNCTION__, "cannot set realm name to %s (realm already active)",realmname);
135 			return -1;
136 		}
137 		realm_set_name(realm,realmname);
138         }
139 	version=prefs_get_d2cs_version();
140 	try_version=bn_int_get(packet->u.d2cs_bnetd_authreply.version);
141 	if (version && version != try_version) {
142 		eventlog(eventlog_level_error,__FUNCTION__,"d2cs version mismatch 0x%X - 0x%X",
143 			try_version,version);
144 		reply=BNETD_D2CS_AUTHREPLY_BAD_VERSION;
145 	} else {
146 		reply=BNETD_D2CS_AUTHREPLY_SUCCEED;
147 	}
148 
149 	if (reply==BNETD_D2CS_AUTHREPLY_SUCCEED) {
150 		eventlog(eventlog_level_info,__FUNCTION__,"d2cs %s authed",
151 			addr_num_to_ip_str(conn_get_addr(c)));
152 		conn_set_state(c,conn_state_loggedin);
153 		realm_active(realm,c);
154 	} else {
155 		eventlog(eventlog_level_error,__FUNCTION__,"failed to auth d2cs %s",
156 			addr_num_to_ip_str(conn_get_addr(c)));
157 	}
158 	if ((rpacket=packet_create(packet_class_d2cs_bnetd))) {
159 		packet_set_size(rpacket,sizeof(t_bnetd_d2cs_authreply));
160 		packet_set_type(rpacket,BNETD_D2CS_AUTHREPLY);
161 		bn_int_set(&rpacket->u.bnetd_d2cs_authreply.h.seqno,1);
162 		bn_int_set(&rpacket->u.bnetd_d2cs_authreply.reply,reply);
163 		conn_push_outqueue(c,rpacket);
164 		packet_del_ref(rpacket);
165 	}
166 	return 0;
167 }
168 
on_d2cs_accountloginreq(t_connection * c,t_packet const * packet)169 static int on_d2cs_accountloginreq(t_connection * c, t_packet const * packet)
170 {
171 	unsigned int	sessionkey;
172 	unsigned int	sessionnum;
173 	unsigned int	salt;
174 	char const *	account;
175 	char const *	tname;
176 	t_connection	* client;
177 	int		reply;
178 	t_packet	* rpacket;
179 	struct
180 	{
181 		bn_int   salt;
182 		bn_int   sessionkey;
183 		bn_int   sessionnum;
184 		bn_int   secret;
185 		bn_int	 passhash[5];
186 	} temp;
187 	t_hash       secret_hash;
188 	char const * pass_str;
189 	t_hash	     passhash;
190 	t_hash	     try_hash;
191 
192 	if (packet_get_size(packet)<sizeof(t_d2cs_bnetd_accountloginreq)) {
193 		eventlog(eventlog_level_error,__FUNCTION__,"got bad packet size");
194 		return -1;
195 	}
196 	if (!(account=packet_get_str_const(packet,sizeof(t_d2cs_bnetd_accountloginreq),USER_NAME_MAX))) {
197 		eventlog(eventlog_level_error,__FUNCTION__,"missing or too long account name");
198 		return -1;
199 	}
200 	sessionkey=bn_int_get(packet->u.d2cs_bnetd_accountloginreq.sessionkey);
201 	sessionnum=bn_int_get(packet->u.d2cs_bnetd_accountloginreq.sessionnum);
202 	salt=bn_int_get(packet->u.d2cs_bnetd_accountloginreq.seqno);
203 	if (!(client=connlist_find_connection_by_sessionnum(sessionnum))) {
204 		eventlog(eventlog_level_error,__FUNCTION__,"sessionnum %d not found",sessionnum);
205 		reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED;
206 	} else if (sessionkey!=conn_get_sessionkey(client)) {
207 		eventlog(eventlog_level_error,__FUNCTION__,"sessionkey %d not match",sessionkey);
208 		reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED;
209 	} else if (!(tname=conn_get_username(client))) {
210 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL username");
211 		reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED;
212 	} else if (strcasecmp(account,tname)) {
213 		eventlog(eventlog_level_error,__FUNCTION__,"username %s not match",account);
214 		reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED;
215 	} else {
216 		bn_int_set(&temp.salt,salt);
217 		bn_int_set(&temp.sessionkey,sessionkey);
218 		bn_int_set(&temp.sessionnum,sessionnum);
219 		bn_int_set(&temp.secret,conn_get_secret(client));
220 		pass_str=account_get_pass(conn_get_account(client));
221 		if (hash_set_str(&passhash,pass_str)<0) {
222 			reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED;
223 		} else {
224 			hash_to_bnhash((t_hash const *)&passhash,temp.passhash);
225 			bnet_hash(&secret_hash,sizeof(temp),&temp);
226 			bnhash_to_hash(packet->u.d2cs_bnetd_accountloginreq.secret_hash,&try_hash);
227 			if (hash_eq(try_hash,secret_hash)==1) {
228 				eventlog(eventlog_level_debug,__FUNCTION__,"user %s loggedin on d2cs",
229 					account);
230 				reply=BNETD_D2CS_ACCOUNTLOGINREPLY_SUCCEED;
231 			} else {
232 				eventlog(eventlog_level_error,__FUNCTION__,"user %s hash not match",
233 					account);
234 				reply=BNETD_D2CS_ACCOUNTLOGINREPLY_FAILED;
235 			}
236 		}
237 	}
238 	if ((rpacket=packet_create(packet_class_d2cs_bnetd))) {
239 		packet_set_size(rpacket,sizeof(t_bnetd_d2cs_accountloginreply));
240 		packet_set_type(rpacket,BNETD_D2CS_ACCOUNTLOGINREPLY);
241 		bn_int_set(&rpacket->u.bnetd_d2cs_accountloginreply.h.seqno,
242 			bn_int_get(packet->u.d2cs_bnetd_accountloginreq.h.seqno));
243 		bn_int_set(&rpacket->u.bnetd_d2cs_accountloginreply.reply,reply);
244 		conn_push_outqueue(c,rpacket);
245 		packet_del_ref(rpacket);
246 	}
247 	return 0;
248 }
249 
250 #define CHAR_PORTRAIT_LEN	0x30
on_d2cs_charloginreq(t_connection * c,t_packet const * packet)251 static int on_d2cs_charloginreq(t_connection * c, t_packet const * packet)
252 {
253 	t_connection *	client;
254 	char const *	charname;
255 	char const *	portrait;
256 	char const *	clienttag;
257 	char *	temp;
258 	unsigned int	sessionnum;
259 	t_realm * 	realm;
260 	char const *	realmname;
261 	unsigned int	pos, reply;
262 	t_packet *	rpacket;
263 
264 	if (packet_get_size(packet)<sizeof(t_d2cs_bnetd_charloginreq)) {
265 		eventlog(eventlog_level_error,__FUNCTION__,"got bad packet size");
266 		return -1;
267 	}
268 	sessionnum=bn_int_get(packet->u.d2cs_bnetd_charloginreq.sessionnum);
269 	pos=sizeof(t_d2cs_bnetd_charloginreq);
270 	if (!(charname=packet_get_str_const(packet,pos,CHAR_NAME_LEN))) {
271 		eventlog(eventlog_level_error,__FUNCTION__,"got bad character name");
272 		return -1;
273 	}
274 	pos+=strlen(charname)+1;
275 	if (!(portrait=packet_get_str_const(packet,pos,CHAR_PORTRAIT_LEN))) {
276 		eventlog(eventlog_level_error,__FUNCTION__,"got bad character portrait");
277 		return -1;
278 	}
279 	if (!(client=connlist_find_connection_by_sessionnum(sessionnum))) {
280 		eventlog(eventlog_level_error,__FUNCTION__,"user %d not found",sessionnum);
281 		reply = BNETD_D2CS_CHARLOGINREPLY_FAILED;
282 	} else if (!(clienttag=clienttag_uint_to_str(conn_get_clienttag(client)))) {
283 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL clienttag");
284 		reply = BNETD_D2CS_CHARLOGINREPLY_FAILED;
285 	} else if (!(realm=conn_get_realm(client))) {
286 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL realm");
287 		reply = BNETD_D2CS_CHARLOGINREPLY_FAILED;
288 	} else {
289 		char revtag[8];
290 
291 		realmname = realm_get_name(realm);
292 		temp=xmalloc(strlen(clienttag)+strlen(realmname)+1+strlen(charname)+1+
293 			strlen(portrait)+1);
294 		reply = BNETD_D2CS_CHARLOGINREPLY_SUCCEED;
295 		strcpy(revtag,clienttag);
296 		strreverse(revtag);
297 		sprintf(temp,"%4s%s,%s,%s",revtag,realmname,charname,portrait);
298 		conn_set_charname(client,charname);
299 		conn_set_realminfo(client,temp);
300 		xfree(temp);
301 		eventlog(eventlog_level_debug,__FUNCTION__,
302 			"loaded portrait for character %s",charname);
303 	}
304 	if ((rpacket=packet_create(packet_class_d2cs_bnetd))) {
305 		packet_set_size(rpacket,sizeof(t_bnetd_d2cs_charloginreply));
306 		packet_set_type(rpacket,BNETD_D2CS_CHARLOGINREPLY);
307 		bn_int_set(&rpacket->u.bnetd_d2cs_charloginreply.h.seqno,
308 			bn_int_get(packet->u.d2cs_bnetd_charloginreq.h.seqno));
309 		bn_int_set(&rpacket->u.bnetd_d2cs_charloginreply.reply,reply);
310 		conn_push_outqueue(c,rpacket);
311 		packet_del_ref(rpacket);
312 	}
313 	return 0;
314 }
315 
handle_d2cs_init(t_connection * c)316 extern int handle_d2cs_init(t_connection * c)
317 {
318 	t_packet	* packet;
319 
320 	if ((packet=packet_create(packet_class_d2cs_bnetd))) {
321 		packet_set_size(packet,sizeof(t_bnetd_d2cs_authreq));
322 		packet_set_type(packet,BNETD_D2CS_AUTHREQ);
323 		bn_int_set(&packet->u.bnetd_d2cs_authreq.h.seqno,1);
324 		bn_int_set(&packet->u.bnetd_d2cs_authreq.sessionnum,conn_get_sessionnum(c));
325 		conn_push_outqueue(c,packet);
326 		packet_del_ref(packet);
327 	}
328 	eventlog(eventlog_level_info,__FUNCTION__,"sent init packet to d2cs (sessionnum=%d)",
329 		conn_get_sessionnum(c));
330 	return 0;
331 }
332 
send_d2cs_gameinforeq(t_connection * c)333 extern int send_d2cs_gameinforeq(t_connection * c)
334 {
335 	t_packet	* packet;
336 	t_game		* game;
337 	t_realm		* realm;
338 
339 	if (!(c))
340 	{
341 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL conn");
342 		return -1;
343 	}
344 
345 	if (!(game = conn_get_game(c)))
346 	{
347 		eventlog(eventlog_level_error,__FUNCTION__,"conn had NULL game");
348 		return -1;
349 	}
350 
351 	if (!(realm = conn_get_realm(c)))
352 	{
353 		eventlog(eventlog_level_error,__FUNCTION__,"conn had NULL realm");
354 		return -1;
355 	}
356 
357 
358 	if ((packet=packet_create(packet_class_d2cs_bnetd))) {
359 		packet_set_size(packet,sizeof(t_bnetd_d2cs_gameinforeq));
360 		packet_set_type(packet,BNETD_D2CS_GAMEINFOREQ);
361 		bn_int_set(&packet->u.bnetd_d2cs_gameinforeq.h.seqno,0);
362 		packet_append_string(packet,game_get_name(game));
363 		conn_push_outqueue(realm_get_conn(realm),packet);
364 		packet_del_ref(packet);
365 	}
366 	return 0;
367 }
368 
on_d2cs_gameinforeply(t_connection * c,t_packet const * packet)369 static int on_d2cs_gameinforeply(t_connection * c, t_packet const * packet)
370 {
371 	t_game *		game;
372 	char const *		gamename;
373 	unsigned int		difficulty;
374 	t_game_difficulty	diff;
375 
376 	if (!(c)) {
377 		eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
378 		return -1;
379 	}
380 
381 	if (!(gamename = packet_get_str_const(packet,sizeof(t_d2cs_bnetd_gameinforeply),GAME_NAME_LEN)))
382 	{
383 		eventlog(eventlog_level_error,__FUNCTION__,"missing or too long gamename");
384 		return -1;
385 	}
386 
387 	if (!(game = gamelist_find_game(gamename,CLIENTTAG_DIABLO2DV_UINT,game_type_diablo2closed))
388 	    && !(game = gamelist_find_game(gamename,CLIENTTAG_DIABLO2XP_UINT,game_type_diablo2closed)))
389 	{
390 	       eventlog(eventlog_level_error,__FUNCTION__,"reply for unknown game \"%s\"",gamename);
391                return -1;
392 	}
393 
394 	difficulty = bn_byte_get(packet->u.d2cs_bnetd_gameinforeply.difficulty);
395 
396 	switch (difficulty)
397 	{
398 		case 0:
399 			diff = game_difficulty_normal;
400 			break;
401 		case 1:
402 			diff = game_difficulty_nightmare;
403 			break;
404 		case 2:
405 			diff = game_difficulty_hell;
406 			break;
407 		default:
408 			diff = game_difficulty_none;
409 	}
410 
411 	game_set_difficulty(game,diff);
412 
413 	return 0;
414 }
415 
416