void NetClient::calc() { ENetEvent event; if (me && server) while (enet_host_service(me, &event, 0) > 0) { char type = (event.type == ENET_EVENT_TYPE_RECEIVE) ? (char) event.packet->data[0] : LEMNET_NOTHING; if (event.type == ENET_EVENT_TYPE_CONNECT) { // Server shall check if protocols and versions match. send_welcome_data(); } else if (event.type == ENET_EVENT_TYPE_DISCONNECT) { exit = true; } else if (type == LEMNET_DISCON_SILENT) { exit = true; } else if (type == LEMNET_WELCOME_DATA) { // The server sends out packets of this sort, but that only // happens to inform clients before 2009 Oct 25 of their very // old version. The server will probably stop that in some // months, and until then we simply ignore these. } else if (type == LEMNET_YOU_TOO_OLD || type == LEMNET_YOU_TOO_NEW) { const bool old = (type == LEMNET_YOU_TOO_OLD); if (old) Console::push_back(Language::net_chat_we_too_old); else Console::push_back(Language::net_chat_we_too_new); std::string msg = Language::net_chat_version_yours; msg += Help::version_to_string(gloB->version); msg += Language::net_chat_version_server; msg += Help::version_to_string( get_uint32_from(event.packet->data + 2)); msg += '.'; Console::push_back(msg); if (old) Console::push_back(Language::net_chat_please_download); else Console::push_back(Language::net_chat_server_update); // The server will throw us out. } else if (type == LEMNET_SOMEONE_OLD || type == LEMNET_SOMEONE_NEW) { int who_nr = event.packet->data[1]; PlDatIt who = players.begin(); while (who != players.end() && who->number != who_nr) ++who; if (type == LEMNET_SOMEONE_NEW) { Console::push_back(Language::net_chat_someone_new); Console::push_back(Language::net_chat_server_update); } // There exists someone with that number else if (who != players.end()) { std::string s = who->name + ' ' + Language::net_chat_named_guy_old; Console::push_back(s); } else Console::push_back(Language::net_chat_someone_old); // This is independent from new or old if (who != players.end()) { // reset player players.erase(who); set_nobody_ready(); player_data_change = true; } } else if (type == LEMNET_RECHECK) { send_welcome_data(); } else if (type == LEMNET_ASSIGN_NUMBER) { PlayerData pd(event.packet->data[2], gloB->user_name.c_str()); pd.style = pd.number % (LixEn::STYLE_MAX-1) + 1; if (useR->network_last_style > 0 && useR->network_last_style < LixEn::STYLE_MAX) pd.style = static_cast <char> (useR->network_last_style); // The lobby saves the style to useR->... players.clear(); players.push_back(pd); ourself = players.begin(); ENetPacket* p = pd.create_packet(); enet_peer_send(server, LEMNET_CHANNEL_MAIN, p); } else if (type == LEMNET_ROOM_DATA) { rooms.clear(); for (int room = 0; room < NETWORK_ROOMS_MAX; ++room) if (event.packet->data[2 + room] > 0) { RoomData rd; rd.number = room; rd.members = event.packet->data[2 + room]; rooms.insert(rd); } room_data_change = true; } else if (type == LEMNET_ROOM_CHANGE) { // Throw out all people other than us, because we'll soon receive // LEMNET_PLAYER_BEFORE packets for everyone in the new room. PlayerData temp_ourself = *ourself; players.clear(); players.push_back(temp_ourself); ourself = players.begin(); const char target_room = event.packet->data[2]; if (ourself->room == -1) Console::push_back(Language::net_chat_we_connected); else if (target_room == 0) Console::push_back(Language::net_chat_we_in_lobby); else { std::ostringstream message; message << Language::net_chat_we_in_room << (int) target_room << Language::net_chat_we_in_room_2; Console::push_back(message.str()); } ourself->room = target_room; } else if (type == LEMNET_PLAYER_DATA || type == LEMNET_PLAYER_BEFORE) { PlayerData pd; pd.read_from(event.packet); PlDatIt known = get_player(pd.number); // A new person has joined if (known == players.end()) { // Insert new data before the first higher data, so that // the players list is still sorted by operator <. PlDatIt insert_here = --players.end(); while (insert_here != --players.begin() && pd < *insert_here) --insert_here; players.insert(++insert_here, pd); set_nobody_ready(); if (type == LEMNET_PLAYER_DATA) { std::ostringstream message; message << pd.name; if (ourself->room == 0) message << Language::net_chat_player_in_lobby; else { message << Language::net_chat_player_in_room; message << (int) ourself->room; message << Language::net_chat_player_in_room_2; } Console::push_back(message.str()); Sound::play_loud(Sound::JOIN); } } else { // Only the information about readiness doesn't set back // all ready states. Everything else does. // snr = set nobody ready bool dont_call_snr = *known == pd; known->ready = !known->ready; // *known will be overwr. anyway dont_call_snr = dont_call_snr || *known == pd; *known = pd; if (!dont_call_snr) set_nobody_ready(); } player_data_change = true; } else if (type == LEMNET_PLAYER_CLEAR || type == LEMNET_PLAYER_OUT_TO) { unsigned nr = event.packet->data[1]; PlDatIt who = get_player(nr); if (who != players.end()) { std::ostringstream message; message << who->name; if (type == LEMNET_PLAYER_CLEAR) { message << Language::net_chat_disconnection; } else { int room = event.packet->data[2]; if (room == 0) { message << Language::net_chat_player_out_lobby; } else { message << Language::net_chat_player_out_room; message << room; message << Language::net_chat_player_out_room_2; } } Console::push_back(message.str()); players.erase(who); player_data_change = true; set_nobody_ready(); } } else if (type == LEMNET_LEVEL_FILE) { set_nobody_ready(); std::istringstream str((char*) event.packet->data + 2); level.load_from_stream(str); level_change = true; PlDatIt who = get_player(event.packet->data[1]); if (who != players.end()) { std::ostringstream msg; msg << who->name << ' ' << Language::net_chat_level_change << ' ' << level.get_name(); Console::push_back(msg.str()); } } else if (type == LEMNET_CHAT_MESSAGE) { std::string message = (const char*) &event.packet->data[2]; Console::push_back(message, true); // true = weiss } else if (type == LEMNET_GAME_START) { // Reset things to make it possible to play multiple games one // after another set_nobody_ready(); replay_data.clear(); permu = Permu(event.packet->data[2],(char*)event.packet->data + 3); // Jetzt aber... auf geht's! game_start = true; updates_change = 0; std::ostringstream start_message; start_message << Language::net_game_start; if (useR->key_chat > 0 && useR->key_chat < KEY_MAX) { start_message << Language::net_game_how_to_chat_1; start_message << Help::scancode_to_string(useR->key_chat); start_message << Language::net_game_how_to_chat_2; } Console::push_back(start_message.str()); } else if (type == LEMNET_GAME_END) { game_end = true; Console::push_back(Language::net_game_end); } else if (type == LEMNET_REPLAY_DATA) { // Only make available if we aren't the sender if (event.packet->data[1] != ourself->number) { ReplayData data; data.read_from(event.packet); replay_data.push_back(data); } } else if (type == LEMNET_UPDATES) { updates_change = get_uint32_from(&event.packet->data[2]); } if (event.type == ENET_EVENT_TYPE_RECEIVE) enet_packet_destroy(event.packet); } }
void *thread_operator(void *attr){ int rc; int my_id = (int) attr; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); void *socket = zmq_socket(server_pool.context, ZMQ_REP); zmq_connect(socket, ZMQ_INPROC_ADDR); pthread_cleanup_push((void (*)(void *)) zmq_close, socket); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); zmq_msg_t reply_msgs[DCS_SERVER_REPLY_COUNT]; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); for(int i = 0; i < DCS_SERVER_REPLY_COUNT; i++){ zmq_msg_init_size(&reply_msgs[i], DCS_SERVER_REPLY_SIZE); memcpy(zmq_msg_data(&reply_msgs[i]), &server_replys[i], DCS_SERVER_REPLY_SIZE); } pthread_cleanup_push(thread_operator_msg_clean, reply_msgs); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if(!server_pool.no_barr){ rc = pthread_barrier_wait(&server_pool.proxy_barr); if(rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) syslog(LOG_ERR, "Thread #%d cannot wait on barrier.", my_id); } while(1){ int reply_id = DCS_SERVER_REPLY_OK; zmq_msg_t client_msg; char *message; size_t msg_size; unsigned char digest[MSG_DIGEST_SIZE]; char *domain; char *md5sum; char *sep; HASH_ELEMENT *comp; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); zmq_msg_init(&client_msg); pthread_cleanup_push((void (*)(void *)) zmq_msg_close, &client_msg); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); zmq_msg_recv(&client_msg, socket, 0); message = (char *) zmq_msg_data(&client_msg); msg_size = zmq_msg_size(&client_msg); DEBUGMSG(syslog(LOG_DEBUG, "msg_size = %d\n", msg_size)); if(msg_size >= MSG_DIGEST_SIZE + 1 + 1 + 2){ //~ проверка размера сообщения здесь!!! //~ decrypt and verify message here! //~ message = func(message) memset(digest, '\0', MSG_DIGEST_SIZE); rc = msg_digest(message + MSG_DIGEST_SIZE, MSG_SALT_PATH, msg_size - MSG_DIGEST_SIZE, digest); if(rc){ DEBUGMSG(syslog(LOG_DEBUG, "msg_digest failed!!!")); } if(memcmp(message, digest, MSG_DIGEST_SIZE) == 0){ message += MSG_DIGEST_SIZE; DEBUGMSG(syslog(LOG_DEBUG, "Thread #%d catch message: '%s'\n", my_id, message + 1)); switch(*message){ case DCS_CLIENT_REQ_MD5: message++; sep = strchr(message, MSG_SEPARATOR); if(sep){ *sep = '\0'; domain = message; md5sum = sep + 1; /* Проверки на длину md5-сум!!! */ comp = Hash_find(&server_pool.hash, domain, (size_t) (unsigned int) sep - (unsigned int) message); if(comp){ if(memcmp(md5sum, comp->val, HASH_ELEMENT_VAL_SIZE) != 0){ /* Суммы различны, подать сюда полный список деталей! */ reply_id = DCS_SERVER_REPLY_FULL; DEBUGMSG(syslog(LOG_DEBUG, "Суммы различны\n")); } else{ //~ Суммы совпали, всё хорошо. reply_id = DCS_SERVER_REPLY_OK; } } else{ /* Компьютера в хэше нет. */ reply_id = DCS_SERVER_REPLY_FULL; DEBUGMSG(syslog(LOG_DEBUG, "Компьютера в хэше нет\n")); } } break; case DCS_CLIENT_REQ_FULL: message++; sep = strchr(message, MSG_SEPARATOR); if(sep){ *sep = '\0'; domain = message; size_t domain_size; CL_Detail *details; size_t details_count; unsigned char *hwdata = (unsigned char *) sep + 1; msg_size -= (MSG_DIGEST_SIZE + 2 + ( (size_t) (unsigned int) sep - (unsigned int) message)); domain_size = (size_t) ((unsigned int) sep - (unsigned int) message); /* Считаем md5 */ MD5_CTX mdcontext; MD5Init(&mdcontext); MD5Update(&mdcontext, hwdata, msg_size); MD5Final(digest, &mdcontext); /* Ищем комп в хэше */ comp = Hash_find(&server_pool.hash, domain, domain_size); if(!comp){ /* Компьютера в хэше нет - новый компьютер. */ DEBUGMSG(syslog(LOG_DEBUG, "Новая машина!")); //~ details = (CL_Detail *) calloc(sizeof(CL_Detail), 40); details = (CL_Detail *) malloc(sizeof(CL_Detail) * 40); unsigned char *hwdata_p = hwdata; for(int i = 0; i < 40; i++){ details[i].vendor_id = get_uint16_from(hwdata_p); hwdata_p += sizeof(details[i].vendor_id); details[i].device_id = get_uint16_from(hwdata_p); hwdata_p += sizeof(details[i].device_id); details[i].subsystem_id = get_uint32_from(hwdata_p); hwdata_p += sizeof(details[i].subsystem_id); details[i].class_code = get_uint32_from(hwdata_p); hwdata_p += sizeof(details[i].class_code); details[i].revision = get_uint8_from(hwdata_p); hwdata_p += sizeof(details[i].revision); memcpy(&details[i].bus_addr, hwdata_p, sizeof(details[i].bus_addr)); hwdata_p += sizeof(details[i].bus_addr); details[i].serial_length = get_uint32_from(hwdata_p); hwdata_p += sizeof(details[i].serial_length); DEBUGMSG(syslog(LOG_DEBUG, "Detail: %.4x:%.4x:%.8x (rev %.2x) [class: %.6x] Bus: '%s', SL '%u'", details[i].vendor_id, details[i].device_id, details[i].subsystem_id, details[i].revision, details[i].class_code, details[i].bus_addr, details[i].serial_length )); memcpy(&details[i].serial, hwdata_p, details[i].serial_length); hwdata_p += details[i].serial_length; details[i].serial[details[i].serial_length] = '\0'; details[i].params_length = get_uint32_from(hwdata_p); hwdata_p += sizeof(details[i].params_length); DEBUGMSG(syslog(LOG_DEBUG, "HERE4! params_length: %d", details[i].params_length)); details[i].params = (char *) calloc(sizeof(char), details[i].params_length); memcpy(details[i].params, hwdata_p, details[i].params_length); hwdata_p += details[i].params_length; details[i].params[details[i].params_length] = '\0'; DEBUGMSG(syslog(LOG_DEBUG, "Detail: %.4x:%.4x:%.8x (%.2x) [%.6x]: '%s', '%s'", details[i].vendor_id, details[i].device_id, details[i].subsystem_id, details[i].revision, details[i].class_code, details[i].serial, details[i].params )); if((unsigned int) (hwdata_p - hwdata) >= msg_size){ details_count = i + 1; details = (CL_Detail *) realloc(details, sizeof(CL_Detail) * details_count); break; } } /* Хэшируем результат */ comp = Hash_insert(&server_pool.hash, domain, domain_size, (char *) digest, MSG_DIGEST_SIZE); if(!comp){ DEBUGMSG(syslog(LOG_DEBUG, "Hash insert error: %d\n", errno)); break; } } else{ /* Есть в кэше, проверим md5 */ if(memcmp(comp->val, digest, HASH_ELEMENT_VAL_SIZE) == 0){ DEBUGMSG(syslog(LOG_DEBUG, "Суммы одинаковые, наверное ошибочный запрос\n")); } else{ /* Суммы различны (так и должно быть) - обновляем! */ memcpy(comp->val, digest, HASH_ELEMENT_VAL_SIZE); } } sync_comp(comp, details, details_count); } break; default: DEBUGMSG(syslog(LOG_DEBUG, "default = %02x\n", *message)); break; } } else{ DEBUGMSG(syslog(LOG_DEBUG, "memcmp failed!")); } } pthread_cleanup_pop(1); /* zmq_msg_close() */ DEBUGMSG(syslog(LOG_DEBUG, "Reply %d: '%.2x'\n", reply_id, *((unsigned int *) zmq_msg_data(&reply_msgs[reply_id])))); zmq_msg_send(&reply_msgs[reply_id], socket, 0); } pthread_cleanup_pop(0); /* thread_operator_msg_clean */ pthread_cleanup_pop(0); /* zmq_close */ pthread_exit(NULL); }