#include "common.h" #include "server_con.h" #include "net.h" #include "arc4.h" #include "options.h" #include "NetMessage.h" #define SERVER_READ_BUFFER_SIZE (1024 * 32) /* TODO: obtain IPs of network interfaces from user's machine, instead of * hardcoding these values below (used in msim_compute_login_response). * This is not immediately * important because you can still connect and perform basic * functions of the protocol. There is also a high chance that the addreses * are RFC1918 private, so the servers couldn't do anything with them * anyways except make note of that fact. Probably important for any * kind of direct connection, or file transfer functionality. */ #define LOGIN_IP_LIST "\x00\x00\x00\x00\x05\x7f\x00\x00\x01\x00\x00\x00\x00\x0a\x00\x00\x40\xc0\xa8\x58\x01\xc0\xa8\x3c\x01" #define LOGIN_IP_LIST_LEN 25 #define NONCE_SIZE 0x20 int status = ID_STATUS_OFFLINE; bool server_running = false, server_stop = false; HANDLE server_connection = 0; int sesskey; void CALLBACK sttMainThreadStatusCallback( ULONG dwParam ) { if(status != dwParam) { int previous_status = status; status = dwParam; ProtoBroadcastAck(MODULE,NULL,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)previous_status, status); } } int waitcallback(unsigned int *timeout) { //PUShowMessage("waitcallback", SM_NOTIFY); return server_stop ? 0 : 1; } int try_ports[9] = {1863, 6660,6661,6662,6665,6668,6669,80,0443}; void try_login(NetMessage &msg, HANDLE connection) { char nonce[NONCE_SIZE * 2 + 2], *nc1 = nonce, *nc2 = nonce + NONCE_SIZE; int size = NONCE_SIZE * 2 + 2; if(msg.get_data("nc", nonce, &size) && size == NONCE_SIZE * 2) { SHA1_INTERFACE sha1; mir_sha1_ctx sha1_ctx; mir_getSHA1I(&sha1); ARC4_INTERFACE arc4; mir_arc4_ctx arc4_ctx; mir_getARC4I(&arc4); char *ch_resp; mir_sha1_byte_t pw_hash[20]; int ch_resp_size; wchar_t wpw[256]; mir_sha1_byte_t key[20]; char email[256]; #ifdef _UNICODE _tcscpy(wpw, options.pw); WideCharToMultiByte(CP_UTF8, 0, options.email, -1, email, 256, 0, 0); #else strcpy(email, options.email); MultiByteToWideChar(code_page, 0, options.pw, -1, wpw, 256); #endif sha1.sha1_hash((mir_sha1_byte_t*)wpw, wcslen(wpw) * sizeof(wchar_t), pw_hash); sha1.sha1_init(&sha1_ctx); sha1.sha1_append(&sha1_ctx, (mir_sha1_byte_t*)pw_hash, 20); sha1.sha1_append(&sha1_ctx, (mir_sha1_byte_t*)nc2, NONCE_SIZE); sha1.sha1_finish(&sha1_ctx, key); arc4.arc4_init(&arc4_ctx, (char *)key, 0x10); ch_resp_size = NONCE_SIZE + strlen(email) + LOGIN_IP_LIST_LEN; ch_resp = new char[ch_resp_size]; memcpy(ch_resp, nc1, NONCE_SIZE); memcpy(ch_resp + NONCE_SIZE, email, strlen(email)); memcpy(ch_resp + NONCE_SIZE + strlen(email), LOGIN_IP_LIST, LOGIN_IP_LIST_LEN); arc4.arc4_crypt(&arc4_ctx, ch_resp, ch_resp, ch_resp_size); ClientNetMessage reply; reply.add_int("login2", 196610); reply.add_string("username", email); reply.add_data("response", ch_resp, ch_resp_size); reply.add_int("clientver", 673); reply.add_int("reconn", 0); reply.add_int("status", 100); reply.add_int("id", 1); delete[] ch_resp; char packet[4096]; int packet_size = reply.make_packet(packet, 4096); Netlib_Send(server_connection, packet, packet_size, MSG_DUMPASTEXT); } else { PUShowMessage("Nonce format error", SM_NOTIFY); } } void __cdecl ServerThreadFunc(void*) { NETLIBOPENCONNECTION conn_data = {0}; conn_data.cbSize = sizeof(NETLIBOPENCONNECTION); conn_data.flags = NLOCF_V2; conn_data.szHost = "im.myspace.akadns.net"; conn_data.wPort = DBGetContactSettingDword(0, MODULE, "LastPort", try_ports[0]); conn_data.timeout = 10; conn_data.waitcallback = waitcallback; int conn_stat = ID_STATUS_CONNECTING; QueueUserAPC(sttMainThreadStatusCallback, mainThread, conn_stat); server_running = true; char *recv_buffer = new char[SERVER_READ_BUFFER_SIZE]; int bytes = 0; char mt[256]; int tries = 0; bool login = true; HANDLE connection = 0; while(!Miranda_Terminated() && !server_stop) { if(login) { if(connection) Netlib_CloseHandle(connection); QueueUserAPC(sttMainThreadStatusCallback, mainThread, conn_stat++); connection = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)hNetlibUser, (LPARAM)&conn_data); } bytes = Netlib_Recv(connection, (char *)recv_buffer, SERVER_READ_BUFFER_SIZE, MSG_DUMPASTEXT); if(bytes == 0) { PUShowMessage("Connection closed", SM_NOTIFY); if(login && tries < 9) { conn_data.wPort = try_ports[tries++]; } else break; } else if(bytes == SOCKET_ERROR) { PUShowMessage("Socket ERROR", SM_NOTIFY); if(login && tries < 9) { conn_data.wPort = try_ports[tries++]; } else break; } else { if(login) { QueueUserAPC(sttMainThreadStatusCallback, mainThread, conn_stat++); login = false; server_connection = connection; DBWriteContactSettingDword(0, MODULE, "LastPort", conn_data.wPort); } //mir_snprintf(mt, 256, "recvd %d bytes", bytes); //PUShowMessage(mt, SM_NOTIFY); NetMessage msg; msg.parse(recv_buffer, bytes); if(msg.exists(NMString("error"))) { char errmsg[256]; if(msg.get_string("errmsg", errmsg, 256)) PUShowMessage(errmsg, SM_WARNING); } else if(msg.get_int("lc") == 1) { QueueUserAPC(sttMainThreadStatusCallback, mainThread, conn_stat++); try_login(msg, server_connection); } else if(msg.get_int("lc") == 2) { sesskey = msg.get_int("sesskey"); QueueUserAPC(sttMainThreadStatusCallback, mainThread, ID_STATUS_ONLINE); } else if(msg.exists(NMString("persistr"))) { /* NMString cmd, dsn, lid; msg.get(NMString("cmd"), cmd); msg.get(NMString("dsn"), dsn); msg.get(NMString("lid"), lid); mir_snprintf(mt, 256, "Peristr message: type is %s,%s,%s", cmd.text, dsn.text, lid.text); PUShowMessage(mt, SM_NOTIFY); */ } } } if(server_connection) { Netlib_CloseHandle(server_connection); server_connection = 0; } delete recv_buffer; QueueUserAPC(sttMainThreadStatusCallback, mainThread, ID_STATUS_OFFLINE); server_running = false; } void StartThread() { if(!server_running) { server_stop = false; mir_forkthread(ServerThreadFunc, 0); } } void StopThread() { if(server_running) { server_stop = true; if(server_connection) { Netlib_CloseHandle(server_connection); server_connection = 0; } } } void InitServerConnection() { StartThread(); } void DeinitServerConnection() { StopThread(); }