00001
00023
00024
00025 #include "debug.h"
00026 #include "internal.h"
00027 #include "server.h"
00028
00029 #include "utils.h"
00030 #include "packet_parse.h"
00031 #include "buddy_info.h"
00032 #include "buddy_list.h"
00033 #include "buddy_status.h"
00034 #include "char_conv.h"
00035 #include "crypt.h"
00036 #include "group.h"
00037 #include "header_info.h"
00038 #include "login_logout.h"
00039 #include "qq_proxy.h"
00040 #include "send_core.h"
00041 #include "qq.h"
00042
00043 #define QQ_LOGIN_DATA_LENGTH 416 //69 //length of plain login packet
00044 #define QQ_LOGIN_REPLY_OK_PACKET_LEN 139
00045 #define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN 11
00046
00047 #define QQ_LOGIN_REPLY_OK 0x00
00048 #define QQ_LOGIN_REPLY_REDIRECT 0x01
00049 #define QQ_LOGIN_REPLY_PWD_ERROR 0x02
00050 #define QQ_LOGIN_REPLY_MISC_ERROR 0xff // defined by myself
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068 static const guint8 login_23_51[29] = {
00069 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00070 0x00, 0x00, 0x00, 0x00, 0x29, 0xc0, 0xf8, 0xc4,
00071 0x04, 0x3b, 0xee, 0x57, 0x92, 0xd2, 0x42, 0xa6,
00072 0xbe, 0x41, 0x98, 0x97, 0x9e
00073 };
00074
00075
00076
00077
00078
00079
00080
00081 static const guint8 login_53_68[16] = {
00082 0x2e, 0xda, 0x0c, 0x59, 0xa7, 0x1a, 0xd6, 0x4a,
00083 0xb1, 0x48, 0x5d, 0xba, 0x37, 0x1e, 0xac, 0xb9
00084 };
00085
00086 static const guint8 login_94_118[25] = {
00087 0x01, 0x40, 0x01, 0xd7, 0x50, 0x72, 0xc8, 0x00,
00088 0x10, 0x4a, 0xc3, 0x1b, 0x6c, 0xf9, 0x85, 0xf5,
00089 0xd9, 0xa8, 0x05, 0xac, 0x95, 0xa0, 0xe2, 0x44,
00090 0x01
00091 };
00092
00093 typedef struct _qq_login_reply_ok qq_login_reply_ok_packet;
00094 typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
00095
00096 struct _qq_login_reply_ok {
00097 guint8 result;
00098 guint8 *session_key;
00099 guint32 uid;
00100 guint8 client_ip[4];
00101 guint16 client_port;
00102 guint8 server_ip[4];
00103 guint16 server_port;
00104 time_t login_time;
00105 guint8 unknown1[26];
00106 guint8 unknown_server1_ip[4];
00107 guint16 unknown_server1_port;
00108 guint8 unknown_server2_ip[4];
00109 guint16 unknown_server2_port;
00110 guint16 unknown2;
00111 guint16 unknown3;
00112 guint8 unknown4[32];
00113 guint8 unknown5[12];
00114 guint8 last_client_ip[4];
00115 time_t last_login_time;
00116 guint8 unknown6[8];
00117 };
00118
00119 struct _qq_login_reply_redirect {
00120 guint8 result;
00121 guint32 uid;
00122 guint8 new_server_ip[4];
00123 guint16 new_server_port;
00124 };
00125
00126 extern gint
00127 _create_packet_head_seq(guint8 * buf,
00128 guint8 ** cursor, GaimConnection * gc, guint16 cmd, gboolean is_auto_seq, guint16 * seq);
00129 extern gint
00130 _qq_send_packet(GaimConnection * gc, guint8 * buf, gint len, guint16 cmd);
00131
00132
00133
00134
00135 static gchar *_gen_login_key(void)
00136 {
00137 return g_strnfill(QQ_KEY_LENGTH, 0x01);
00138 }
00139
00140
00141
00142 static gint _qq_process_login_ok(GaimConnection * gc, guint8 * data, gint len)
00143 {
00144 gint bytes;
00145 guint8 *cursor;
00146 qq_data *qd;
00147 qq_login_reply_ok_packet lrop;
00148
00149 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_MISC_ERROR);
00150
00151 qd = (qq_data *) gc->proto_data;
00152 cursor = data;
00153 bytes = 0;
00154
00155
00156 bytes += read_packet_b(data, &cursor, len, &lrop.result);
00157
00158 lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH);
00159 cursor += QQ_KEY_LENGTH;
00160 bytes += QQ_KEY_LENGTH;
00161 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Get session_key done\n");
00162
00163 bytes += read_packet_dw(data, &cursor, len, &lrop.uid);
00164
00165 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.client_ip, 4);
00166
00167 bytes += read_packet_w(data, &cursor, len, &lrop.client_port);
00168
00169 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.server_ip, 4);
00170
00171 bytes += read_packet_w(data, &cursor, len, &lrop.server_port);
00172
00173 bytes += read_packet_dw(data, &cursor, len, (guint32 *) & lrop.login_time);
00174
00175 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.unknown1, 26);
00176
00177 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.unknown_server1_ip, 4);
00178
00179 bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port);
00180
00181 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.unknown_server2_ip, 4);
00182
00183 bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port);
00184
00185 bytes += read_packet_w(data, &cursor, len, &lrop.unknown2);
00186
00187 bytes += read_packet_w(data, &cursor, len, &lrop.unknown3);
00188
00189 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.unknown4, 32);
00190
00191 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.unknown5, 12);
00192
00193 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.last_client_ip, 4);
00194
00195 bytes += read_packet_dw(data, &cursor, len, (guint32 *) & lrop.last_login_time);
00196
00197 bytes += read_packet_data(data, &cursor, len, (guint8 *) & lrop.unknown6, 8);
00198
00199 if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {
00200 gaim_debug(GAIM_DEBUG_WARNING, "QQ",
00201 "Fail parsing login info, expect %d bytes, read %d bytes\n",
00202 QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
00203 }
00204
00205 qd->session_key = g_memdup(lrop.session_key, QQ_KEY_LENGTH);
00206 qd->my_ip = gen_ip_str(lrop.client_ip);
00207 qd->my_port = lrop.client_port;
00208 qd->login_time = lrop.login_time;
00209 qd->last_login_time = lrop.last_login_time;
00210 qd->last_login_ip = gen_ip_str(lrop.last_client_ip);
00211
00212 g_free(lrop.session_key);
00213
00214 gaim_connection_set_state(gc, GAIM_CONNECTED);
00215 serv_finish_login(gc);
00216 qd->logged_in = TRUE;
00217
00218
00219 qq_group_init(gc);
00220
00221
00222 qq_send_packet_get_info(gc, qd->uid, FALSE);
00223
00224 qd->status = (qd->login_mode == QQ_LOGIN_MODE_HIDDEN) ? QQ_SELF_STATUS_INVISIBLE : QQ_SELF_STATUS_AVAILABLE;
00225 qq_send_packet_change_status(gc);
00226
00227 qq_send_packet_get_buddies_list(gc, QQ_FRIENDS_LIST_POSITION_START);
00228
00229 return QQ_LOGIN_REPLY_OK;
00230 }
00231
00232
00233
00234 static gint _qq_process_login_redirect(GaimConnection * gc, guint8 * data, gint len)
00235 {
00236 gint bytes, ret;
00237 guint8 *cursor;
00238 gchar *new_server_str;
00239 qq_data *qd;
00240 qq_login_reply_redirect_packet lrrp;
00241
00242 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_MISC_ERROR);
00243
00244 qd = (qq_data *) gc->proto_data;
00245 cursor = data;
00246 bytes = 0;
00247
00248 bytes += read_packet_b(data, &cursor, len, &lrrp.result);
00249
00250 bytes += read_packet_dw(data, &cursor, len, &lrrp.uid);
00251
00252 bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4);
00253
00254 bytes += read_packet_w(data, &cursor, len, &lrrp.new_server_port);
00255
00256 if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
00257 gaim_debug(GAIM_DEBUG_ERROR, "QQ",
00258 "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
00259 QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
00260 ret = QQ_LOGIN_REPLY_MISC_ERROR;
00261 } else {
00262 new_server_str = gen_ip_str(lrrp.new_server_ip);
00263 gaim_debug(GAIM_DEBUG_WARNING, "QQ",
00264 "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port);
00265 qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE);
00266 g_free(new_server_str);
00267 ret = QQ_LOGIN_REPLY_REDIRECT;
00268 }
00269
00270 return ret;
00271 }
00272
00273
00274
00275 static gint _qq_process_login_wrong_pwd(GaimConnection * gc, guint8 * data, gint len)
00276 {
00277 gchar *server_reply, *server_reply_utf8;
00278 server_reply = g_new0(gchar, len);
00279 g_memmove(server_reply, data + 1, len - 1);
00280 server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
00281 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8);
00282 g_free(server_reply);
00283 g_free(server_reply_utf8);
00284
00285 return QQ_LOGIN_REPLY_PWD_ERROR;
00286 }
00287
00288
00289
00290 void qq_send_packet_login(GaimConnection * gc)
00291 {
00292 qq_data *qd;
00293 guint8 *buf, *cursor, *raw_data, *encrypted_data;
00294 guint16 seq_ret;
00295 gint encrypted_len, bytes;
00296
00297 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00298
00299 qd = (qq_data *) gc->proto_data;
00300 buf = g_newa(guint8, MAX_PACKET_SIZE);
00301 raw_data = g_new0(guint8, QQ_LOGIN_DATA_LENGTH);
00302 encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);
00303 qd->inikey = _gen_login_key();
00304
00305
00306
00307 qq_crypt(ENCRYPT, "", 0, qd->pwkey, raw_data, &encrypted_len);
00308
00309 raw_data[16] = 0x00;
00310
00311 *((guint32 *) (raw_data + 17)) = 0x00000000;
00312
00313 *((guint16 *) (raw_data + 21)) = 0x0000;
00314
00315 g_memmove(raw_data + 23, login_23_51, 29);
00316
00317 raw_data[52] = qd->login_mode;
00318
00319 g_memmove(raw_data + 53, login_53_68, 16);
00320
00321 raw_data[69] = qd->token_len;
00322
00323 g_memmove(raw_data + 70, qd->ptoken, qd->token_len);
00324
00325 g_memmove(raw_data + 70 + qd->token_len, login_94_118, 25);
00326 qq_crypt(ENCRYPT, raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len);
00327
00328 cursor = buf;
00329 bytes = 0;
00330 bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret);
00331 bytes += create_packet_dw(buf, &cursor, qd->uid);
00332 bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH);
00333 bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
00334 bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
00335
00336 if (bytes == (cursor - buf))
00337 _qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN);
00338 else
00339 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail create login packet\n");
00340 }
00341
00342
00343
00344 void qq_send_packet_logout(GaimConnection * gc)
00345 {
00346 gint i;
00347 qq_data *qd;
00348
00349 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00350
00351 qd = (qq_data *) gc->proto_data;
00352 for (i = 0; i < 4; i++)
00353 qq_send_cmd(gc, QQ_CMD_LOGOUT, FALSE, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
00354
00355 qd->logged_in = FALSE;
00356 }
00357
00358
00359
00360 void qq_process_login_reply(guint8 * buf, gint buf_len, GaimConnection * gc)
00361 {
00362 gint len, ret, bytes;
00363 guint8 *data;
00364 qq_data *qd;
00365
00366 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00367 g_return_if_fail(buf != NULL && buf_len != 0);
00368
00369 qd = (qq_data *) gc->proto_data;
00370 len = buf_len;
00371 data = g_newa(guint8, len);
00372
00373 if (qq_crypt(DECRYPT, buf, buf_len, qd->pwkey, data, &len)) {
00374
00375 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Decrypt login reply packet with pwkey, %d bytes\n", len);
00376 if (data[0] == QQ_LOGIN_REPLY_OK) {
00377 ret = _qq_process_login_ok(gc, data, len);
00378 } else {
00379 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Unknown login reply code : %d\n", data[0]);
00380 ret = QQ_LOGIN_REPLY_MISC_ERROR;
00381 }
00382 } else {
00383 len = buf_len;
00384 if (qq_crypt(DECRYPT, buf, buf_len, qd->inikey, data, &len)) {
00385
00386 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Decrypt login reply packet with inikey, %d bytes data[0] is %x\n", len, data[0]);
00387 bytes = 0;
00388 switch (data[0]) {
00389 case QQ_LOGIN_REPLY_REDIRECT:
00390 ret = _qq_process_login_redirect(gc, data, len);
00391 break;
00392 case QQ_LOGIN_REPLY_PWD_ERROR:
00393 ret = _qq_process_login_wrong_pwd(gc, data, len);
00394 break;
00395 default:
00396 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
00397 ret = QQ_LOGIN_REPLY_MISC_ERROR;
00398 }
00399 } else {
00400 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "No idea how to decrypt login reply\n");
00401 ret = QQ_LOGIN_REPLY_MISC_ERROR;
00402 }
00403 }
00404
00405 switch (ret) {
00406 case QQ_LOGIN_REPLY_PWD_ERROR:
00407 gaim_connection_error(gc, _("Wrong password!"));
00408 break;
00409 case QQ_LOGIN_REPLY_MISC_ERROR:
00410 gaim_connection_error(gc, _("Unable to login, check debug log"));
00411 break;
00412 case QQ_LOGIN_REPLY_OK:
00413 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Login replys OK, everything is fine\n");
00414 break;
00415 case QQ_LOGIN_REPLY_REDIRECT:
00416
00417 break;
00418 default:{;
00419 }
00420 }
00421 }
00422
00423
00424 void qq_process_login_token_relay(guint8 * buf, gint buf_len, GaimConnection * gc)
00425 {
00426 gint bytes;
00427 guint8 retCode = 0;
00428 guint8 *cursor;
00429 qq_data *qd;
00430 if (NULL ==gc || NULL == gc->proto_data)
00431 return;
00432 qd = (qq_data *) gc->proto_data;
00433 cursor = buf;
00434 bytes = 0;
00435
00436
00437 bytes += read_packet_b(buf, &cursor, buf_len, &retCode);
00438 if (retCode != 0)
00439 {
00440 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail get login tocken from server\n");
00441 return;
00442 }
00443 bytes += read_packet_b(buf, &cursor, buf_len, &qd->token_len);
00444 if (qd->ptoken != NULL)
00445 g_free(qd->ptoken);
00446 qd->ptoken = g_new0(guint8, qd->token_len);
00447 bytes += read_packet_data(buf, &cursor, buf_len, qd->ptoken, qd->token_len);
00448 qq_send_packet_login(gc);
00449
00450 }
00451
00452
00453 void qq_send_packet_login_token(GaimConnection * gc)
00454 {
00455 qq_data *qd;
00456 guint8 *buf, *cursor;
00457 gint bytes;
00458 guint16 seq_ret;
00459 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00460 qd = (qq_data *) gc->proto_data;
00461 buf = g_newa(guint8, MAX_PACKET_SIZE);
00462 cursor = buf;
00463 qd->token_len = 0;
00464 qd->ptoken = NULL;
00465 bytes = 0;
00466 bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_GET_LOGIN_TOKEN, TRUE, &seq_ret);
00467 bytes += create_packet_dw(buf, &cursor, qd->uid);
00468 bytes += create_packet_b(buf, &cursor, 0x00);
00469 bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
00470 if (bytes == (cursor - buf))
00471 _qq_send_packet(gc, buf, bytes, QQ_CMD_GET_LOGIN_TOKEN);
00472 else
00473 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail create login tocken packet\n");
00474 }
00475
00476
00477