static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) { PurpleConnection *gc = NULL; qq_data *qd; guint8 *buf; gint buf_len; gc = (PurpleConnection *) data; g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; if(cond != PURPLE_INPUT_READ) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Socket error")); return; } buf = g_newa(guint8, MAX_PACKET_SIZE); /* here we have UDP proxy suppport */ buf_len = read(source, buf, MAX_PACKET_SIZE); if (buf_len <= 0) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to read from socket")); return; } /* keep alive will be sent in 30 seconds since last_receive * QQ need a keep alive packet in every 60 seconds gc->last_received = time(NULL); */ if (buf_len < QQ_UDP_HEADER_LENGTH) { if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) { qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING", buf, buf_len, "Received packet is too short, or no header and tail tag"); return; } } /* packet_process may call disconnect and destory data like conn * do not call packet_process before jump, * break if packet_process return FALSE */ packet_process(gc, buf, buf_len); }
/* default process, decrypt and dump */ static void process_unknown_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq) { gchar *msg; g_return_if_fail(data != NULL && data_len != 0); qq_show_packet(title, data, data_len); qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, ">>> [%d] %s -> [default] decrypt and dump", seq, qq_get_cmd_desc(cmd)); msg = g_strdup_printf("Unknown command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd)); //purple_notify_info(gc, _("QQ Error"), title, msg); g_free(msg); }
static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint data_len) { gint bytes ; gint decryped_bytes; qq_file_header fh; guint8 *decrypted_data; gint decrypted_len; qq_data *qd = (qq_data *) gc->proto_data; guint16 packet_type; guint16 seq; guint8 hellobyte; ft_info *info = (ft_info *) qd->xfer->data; bytes = 0; bytes += _qq_get_file_header(&fh, data + bytes); decrypted_data = g_newa(guint8, data_len); decrypted_len = qq_decrypt(decrypted_data, data, data_len, qd->session_md5); if ( decrypted_len <= 0 ) { purple_debug_error("QQ", "Error decrypt rcv file ctrl packet\n"); return; } /* only for debug info */ decryped_bytes = 16; /* skip md5 section */ decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes); decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes); decryped_bytes += 4+1+1+19+1; /* skip something */ purple_debug_info("QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type)); qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", decrypted_data, decrypted_len, "decrypted control packet received:"); switch (packet_type) { case QQ_FILE_CMD_NOTIFY_IP_ACK: decryped_bytes = 0; qq_get_conn_info(info, decrypted_data + decryped_bytes); /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); break; case QQ_FILE_CMD_SENDER_SAY_HELLO: /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ decryped_bytes += 47; decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0); break; case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: /* I'm sender, do nothing */ break; case QQ_FILE_CMD_RECEIVER_SAY_HELLO: /* I'm sender, ack the hello packet and send the first data */ decryped_bytes += 47; decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0); break; case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: /* I'm receiver, do nothing */ break; case QQ_FILE_CMD_PING: /* I'm receiver, ack the PING */ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0); break; case QQ_FILE_CMD_PONG: qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); break; default: purple_debug_info("QQ", "unprocess file command %d\n", packet_type); } }
/* send a file to udp channel with QQ_FILE_CONTROL_PACKET_TAG */ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32 to_uid, guint8 hellobyte) { qq_data *qd; gint bytes, bytes_expected, encrypted_len; guint8 *raw_data, *encrypted; time_t now; ft_info *info; qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; raw_data = g_newa (guint8, 61); bytes = 0; now = time(NULL); bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: case QQ_FILE_CMD_NOTIFY_IP_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: bytes += qq_put16(raw_data + bytes, info->send_seq); break; default: bytes += qq_put16(raw_data + bytes, ++qd->send_seq); } bytes += qq_put32(raw_data + bytes, (guint32) now); bytes += qq_put8(raw_data + bytes, 0x00); bytes += qq_put8(raw_data + bytes, qd->my_icon); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put16(raw_data + bytes, 0x0000); bytes += qq_put8(raw_data + bytes, 0x00); /* 0x65: send a file, 0x6b: send a custom face */ bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */ switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: bytes += qq_put8(raw_data + bytes, 0x00); bytes += qq_put8(raw_data + bytes, hellobyte); bytes_expected = 48; break; case QQ_FILE_CMD_PING: case QQ_FILE_CMD_PONG: case QQ_FILE_CMD_NOTIFY_IP_ACK: bytes += qq_fill_conn_info(raw_data, info); bytes_expected = 61; break; default: purple_debug_info("QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n", packet_type); bytes_expected = 0; } if (bytes != bytes_expected) { purple_debug_error("QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d\n", bytes_expected, bytes); return; } qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", raw_data, bytes, "sending packet[%s]:", qq_get_file_cmd_desc(packet_type)); encrypted = g_newa(guint8, bytes + 17); encrypted_len = qq_encrypt(encrypted, raw_data, bytes, info->file_session_key); /*debug: try to decrypt it */ #if 0 guint8 *buf; int buflen; hex_dump = hex_dump_to_str(encrypted, encrypted_len); purple_debug_info("QQ", "encrypted packet: \n%s\n", hex_dump); g_free(hex_dump); buf = g_newa(guint8, MAX_PACKET_SIZE); buflen = encrypted_len; if (qq_crypt(DECRYPT, encrypted, encrypted_len, info->file_session_key, buf, &buflen)) { purple_debug_info("QQ", "decrypt success\n"); if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) purple_debug_info("QQ", "checksum ok\n"); hex_dump = hex_dump_to_str(buf, buflen); purple_debug_info("QQ", "decrypted packet: \n%s\n", hex_dump); g_free(hex_dump); } else { purple_debug_info("QQ", "decrypt fail\n"); } #endif purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); _qq_send_file(gc, encrypted, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid); }