/////////////////////////////////////////// // Set the session tracking state void EQPacket::session_tracking() { m_client2WorldStream->setSessionTracking(showeq_params->session_tracking); m_world2ClientStream->setSessionTracking(showeq_params->session_tracking); m_client2ZoneStream->setSessionTracking(showeq_params->session_tracking); m_zone2ClientStream->setSessionTracking(showeq_params->session_tracking); emit sessionTrackingChanged(showeq_params->session_tracking); }
/////////////////////////////////////////// // Set the session tracking state void EQPacket::session_tracking(bool enable) { m_session_tracking = enable; m_client2WorldStream->setSessionTracking(m_session_tracking); m_world2ClientStream->setSessionTracking(m_session_tracking); m_client2ZoneStream->setSessionTracking(m_session_tracking); m_zone2ClientStream->setSessionTracking(m_session_tracking); emit sessionTrackingChanged(m_session_tracking); }
///////////////////////////////////////////////////// // Handle a protocol level packet. This could be either a top-level // EQUDPIPPacket or a subpacket that is just an EQProtocolPacket. Either way // we use net opcodes here. void EQPacketStream::processPacket(EQProtocolPacket& packet, bool /*isSubpacket*/) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("-->EQPacketStream::processPacket, subpacket=%s on stream %s (%d)", (isSubpacket ? "true" : "false"), EQStreamStr[m_streamid], m_streamid); #endif if (IS_APP_OPCODE(packet.getNetOpCode())) { // This is an app-opcode directly on the wire with no wrapping protocol // information. Weird, but whatever gets the stream read, right? dispatchPacket(packet.payload(), packet.payloadLength(), packet.getNetOpCode(), m_opcodeDB.find(packet.getNetOpCode())); return; } // Process the net opcode switch (packet.getNetOpCode()) { case OP_Combined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found combined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Rolled up multiple packets inside this packet. Need to unroll them // and process them individually. subpacket starts after the net opcode. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; // OpCode (in net order) uint16_t subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Opcode is next. Net opcode or app opcode? if (subOpCode == 0) { // App opcode < 0x00ff. Skip the first byte and dispatch the app // opcode appropriately subpacket++; subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled special app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-3, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); } else if (IS_NET_OPCODE(subOpCode)) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled net opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Net opcode. false = copy. true = subpacket EQProtocolPacket spacket(subpacket, subpacketLength, false, true); processPacket(spacket, true); } else { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); } subpacket += subpacketLength; } } break; case OP_AppCombined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found appcombined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Multiple app op codes in the same packet. Need to unroll and dispatch // them. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; if (subpacketLength != 0xff) { // Dispatch app op code using given packet length. Net order! uint16_t subOpCode = *(uint16_t*)(subpacket); // Handle 3 byte opcodes properly if (subOpCode == 0) { // 3 byte opcode. Drop the first byte, opcode is byte 2 and 3 subpacket++; subpacketLength--; subOpCode = *(uint16_t*)(subpacket); } #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += subpacketLength; } else { // If original length is 0xff, it means it is a long one. The length // is 2 bytes and next. uint16_t longOne = eqntohuint16(subpacket); // Move past the 2 byte length subpacket += 2; // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)subpacket; // Handle 3 byte opcodes properly if (subOpCode == 0) { // 3 byte opcode. Drop the first byte, opcode is byte 2 and 3 subpacket++; longOne--; subOpCode = *(uint16_t*)(subpacket); } #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], longOne-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += longOne; } } } break; case OP_Packet: { // Normal unfragmented sequenced packet. uint16_t seq = packet.arqSeq(); emit seqReceive(seq, (int)m_streamid); // Future packet? if (seq == m_arqSeqExp) { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)(packet.payload()); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x, sub opcode %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode(), subOpCode); #endif // Opcode is next. Net opcode or app opcode? if (subOpCode == 0) { // App opcode < 0x00ff. Skip the first byte and dispatch the app // opcode appropriately subOpCode = *(uint16_t*)&packet.payload()[1]; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("EQPacket: special app opcode extracted for opcode 0000 on stream %s (%d). Opcode %04x", EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&packet.payload()[3], packet.payloadLength()-3, subOpCode, m_opcodeDB.find(subOpCode)); } else if (IS_NET_OPCODE(subOpCode)) { // Net opcode. false = no copy. true = subpacket. EQProtocolPacket spacket(packet.payload(), packet.payloadLength(), false, true); processPacket(spacket, true); } else { // App opcode. Dispatch, skipping opcode. dispatchPacket(&packet.payload()[2], packet.payloadLength()-2, subOpCode, m_opcodeDB.find(subOpCode)); } } else if ((seq > m_arqSeqExp && seq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff))) || seq < (int32_t(m_arqSeqExp) - arqSeqWrapCutoff)) { // Yeah, future packet. Push it on the packet cache. #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { #ifdef PACKET_PROCESS_DIAG // Past packet outside the cut off seqWarn("SEQ: received sequenced %spacket outside expected window on stream %s (%d) netopcode=%04x size=%d. Expecting seq=%04x got seq=%04x, window size %d, dropping packet as in the past.", (isSubpacket ? "sub" : ""), EQStreamStr[m_streamid], m_streamid, packet.getNetOpCode(), packet.payloadLength(), m_arqSeqExp, seq, arqSeqWrapCutoff); #endif } } break; case OP_Oversized: { // Fragmented sequenced data packet. uint16_t seq = packet.arqSeq(); emit seqReceive(seq, (int)m_streamid); // Future packet? if (seq == m_arqSeqExp) { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode()); #endif // Push the fragment on. m_fragment.addFragment(packet); if (m_fragment.isComplete()) { // OpCode from fragment. In network order. uint16_t fragOpCode = *(uint16_t*)(m_fragment.data()); #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: Completed oversized app packet on stream %s with seq %04x, total size %d opcode %04x", EQStreamStr[m_streamid], seq, m_fragment.size()-2, fragOpCode); #endif // dispatch fragment. Skip opcode. if (fragOpCode == 0) { // Special app opcode. Skip first byte and op is byte 2 and 3. fragOpCode = *(uint16_t*)(&m_fragment.data()[1]); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("EQPacket: special app opcode on completed fragment for opcode 0000 on stream %s (%d). Opcode %04x", EQStreamStr[m_streamid], m_streamid, fragOpCode); #endif dispatchPacket(&m_fragment.data()[3], m_fragment.size()-3, fragOpCode, m_opcodeDB.find(fragOpCode)); } else { dispatchPacket(&m_fragment.data()[2], m_fragment.size()-2, fragOpCode, m_opcodeDB.find(fragOpCode)); } m_fragment.reset(); } } else if ((seq > m_arqSeqExp && seq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff))) || seq < (int32_t(m_arqSeqExp) - arqSeqWrapCutoff)) { // Yeah, future packet. Push it on the packet cache. #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { #ifdef PACKET_PROCESS_DIAG // Past packet outside the cut off seqWarn("SEQ: received sequenced %spacket outside expected window on stream %s (%d) netopcode=%04x size=%d. Expecting seq=%04x got seq=%04x, window size %d, dropping packet as in the past.", (isSubpacket ? "sub" : ""), EQStreamStr[m_streamid], m_streamid, packet.getNetOpCode(), packet.payloadLength(), m_arqSeqExp, seq, arqSeqWrapCutoff); #endif } } break; case OP_SessionRequest: { // Session request from client to server. // // Sanity check the size. Don't assume any packet we see is an EQ // session request, since we're gonna cause a huge freakin' malloc // on the maxlength of the session which for some reason some people // won't enjoy! if (packet.payloadLength() != sizeof(SessionRequestStruct)) { // Either SessionRequestStruct changed or this isn't a session // request. #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: Ignoring SessionRequest %s:%u->%s:%u with invalid size %d.", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), packet.payloadLength()); #endif break; } // Pull off session request information SessionRequestStruct* request = (SessionRequestStruct*) packet.payload(); m_sessionId = eqntohuint32((uint8_t*)&(request->sessionId)); m_maxLength = eqntohuint32((uint8_t*)&(request->maxLength)); // Sanity check the max length requested if (m_maxLength > maxPacketSize) { seqWarn("EQPacket: SessionRequest wanted a max packet size of %d which is above our sane max packet size of %d. Using our max", m_maxLength, maxPacketSize); m_maxLength = maxPacketSize; } emit maxLength((int) m_maxLength, (int) m_streamid); #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest %s:%u->%s:%u, sessionId %u maxLength %u, awaiting key for stream %s (%d)", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), m_sessionId, m_maxLength, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionRequest contents: unknown %u, sessionId %u, maxLength %u", eqntohuint32((uint8_t*)&(request->unknown0000)), m_sessionId, m_maxLength); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 2) seqDebug("EQPacket: Raw SessionRequest: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x", packet.payload()[0], packet.payload()[1], packet.payload()[2], packet.payload()[3], packet.payload()[4], packet.payload()[5], packet.payload()[6], packet.payload()[7], packet.payload()[8], packet.payload()[9], packet.payload()[10], packet.payload()[11]); #endif m_arqSeqExp = 0; m_arqSeqFound = true; if (m_session_tracking_enabled) { // Save off client port for the stream so we can match against it // later. SessionRequest should always be an outer protocol packet // so we can cast it to EQUDPIPPacketFormat to get the ip headers. m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getSourcePort(); } } break; case OP_SessionResponse: { // Session response from server // Sanity check the size. Don't assume any packet we see is an EQ // session response, since we're gonna cause a huge freakin' malloc // on the maxlength of the session which for some reason some people // won't enjoy! if (packet.payloadLength() != sizeof(SessionResponseStruct)) { // Either SessionResponseStruct changed or this isn't a session // response. #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: Ignoring SessionResponse %s:%u->%s:%u with invalid size %d.", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), packet.payloadLength()); #endif break; } // Pull off session response information SessionResponseStruct* response = (SessionResponseStruct*) packet.payload(); m_maxLength = eqntohuint32((uint8_t*)&(response->maxLength)); m_sessionKey = eqntohuint32((uint8_t*)&(response->key)); m_sessionId = eqntohuint32((uint8_t*)&(response->sessionId)); // Sanity check the max length requested if (m_maxLength > maxPacketSize) { seqWarn("EQPacket: SessionResponse wanted a max packet size of %d which is above our sane max packet size of %d. Using our max", m_maxLength, maxPacketSize); m_maxLength = maxPacketSize; } emit maxLength((int) m_maxLength, (int) m_streamid); #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse found %s:%u->%s:%u, resetting expected seq, stream %s (%d) (session tracking %s)", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse sessionId %u maxLength %u, key is %u for stream %s (%d)", m_sessionId, m_maxLength, m_sessionKey, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionResponse contents: sessionId %u, key %u, unknown %u, unknown %u, maxLength %u, unknown %u", m_sessionId, m_sessionKey, eqntohuint16((uint8_t*)&(response->unknown0008)), response->unknown0010, m_maxLength, eqntohuint32((uint8_t*) &(response->unknown0015))); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 2) seqDebug("EQPacket: Raw SessionResponse: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x", packet.payload()[0], packet.payload()[1], packet.payload()[2], packet.payload()[3], packet.payload()[4], packet.payload()[5], packet.payload()[6], packet.payload()[7], packet.payload()[8], packet.payload()[9], packet.payload()[10], packet.payload()[11], packet.payload()[12], packet.payload()[13], packet.payload()[14], packet.payload()[15], packet.payload()[16], packet.payload()[17], packet.payload()[18]); #endif // Provide key to corresponding stream from this session/stream emit sessionKey(m_sessionId, m_streamid, m_sessionKey); m_arqSeqExp = 0; m_arqSeqFound = true; // Session tracking if (m_session_tracking_enabled) { // Save off client port for the stream so we can match against it // later. SessionRequest should always be an outer protocol packet // so we can cast it to EQUDPIPPacketFormat to get the ip headers. m_sessionClientPort = ((EQUDPIPPacketFormat&) packet).getDestPort(); // If this is the world server talking to us, reset session tracking if // it is on so we unlatch the client in case of getting kicked. if (m_streamid == world2client) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); } // If this is the zone server talking to us, close the latch and lock else if (m_streamid == zone2client) { // SessionResponse should always be an outer protocol packet, so // the EQProtocolPacket passed in can be cast back to // EQUDPIPPacketFormat, which we need to go to get access to the IP // headers! m_session_tracking_enabled = 2; emit lockOnClient(((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getDestPort()); emit sessionTrackingChanged(m_session_tracking_enabled); } } } break; case OP_SessionDisconnect: { #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionDisconnect found %s:%u->%s:%u, resetting expected seq, stream %s (%d) (session tracking %s)", ((EQUDPIPPacketFormat&) packet).getIPv4SourceA().ascii(), ((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getIPv4DestA().ascii(), ((EQUDPIPPacketFormat&) packet).getDestPort(), EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 2) seqDebug("EQPacket: Raw SessionDisconnect: %02x%02x %02x%02x %02x%02x %02x%02x", packet.payload()[0], packet.payload()[1], packet.payload()[2], packet.payload()[3], packet.payload()[4], packet.payload()[5], packet.payload()[6], packet.payload()[7]); #endif m_arqSeqExp = 0; // Clear cache resetCache(); // Signal closing. Unlatch session tracking if it is on. if (m_session_tracking_enabled) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); m_sessionClientPort = 0; } emit closing(m_sessionId, m_streamid); } break; case OP_Ack: case OP_AckFuture: case OP_AckAfterDisconnect: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on ACK for net opcode %04x seq %04x, stream %s (%d)", packet.getNetOpCode(), eqntohuint16(packet.payload()), EQStreamStr[m_streamid], m_streamid); #endif } break; case OP_KeepAlive: case OP_SessionStatRequest: case OP_SessionStatResponse: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on stats for net opcode %04x, stream %s (%d)", packet.getNetOpCode(), EQStreamStr[m_streamid], m_streamid); #endif } break; default : { seqWarn("EQPacket: Unhandled net opcode %04x, stream %s, size %d", packet.getNetOpCode(), EQStreamStr[m_streamid], packet.payloadLength()); } } }
///////////////////////////////////////////////////////// // Connect the given stream's signals to the proper slots void EQPacket::connectStream(EQPacketStream* stream) { // Packet logging switch (stream->streamID()) { case zone2client: case client2zone: { // Zone server stream connect(stream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(stream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); connect(stream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); } break; case world2client: case client2world: { // World server stream connect(stream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(stream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), this, SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); connect(stream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), this, SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); } break; default : { return; } } // Debugging connect(stream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(stream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(stream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(stream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Session handling connect(stream, SIGNAL(sessionTrackingChanged(uint8_t)), this, SIGNAL(sessionTrackingChanged(uint8_t))); connect(stream, SIGNAL(lockOnClient(in_port_t, in_port_t)), this, SLOT(lockOnClient(in_port_t, in_port_t))); connect(stream, SIGNAL(closing(uint32_t, EQStreamID)), this, SLOT(closeStream(uint32_t, EQStreamID))); connect(stream, SIGNAL(sessionKey(uint32_t, EQStreamID, uint32_t)), this, SLOT(dispatchSessionKey(uint32_t, EQStreamID, uint32_t))); connect(stream, SIGNAL(maxLength(int, int)), this, SIGNAL(maxLength(int, int))); }
//////////////////////////////////////////////////// // handle a new packet on the stream void EQPacketStream::handlePacket(EQUDPIPPacketFormat& packet) { emit numPacket(++m_packetCount, (int)m_streamid); if (!packet.isARQ()) // process packets that don't have an arq sequence { // we only handle packets with opcodes if (packet.payloadLength() < 2) return; // process the packets payload immediately processPayload(packet.payload(), packet.payloadLength()); } else if (packet.isARQ()) // process ARQ sequences { uint16_t arqSeq = packet.arq(); emit seqReceive(arqSeq, (int)m_streamid); /* this conditions should only be met once per zone/world, New Sequence */ if (packet.isSEQStart() && !packet.isClosingLo() && (m_session_tracking_enabled < 2)) { #ifdef PACKET_PROCESS_DIAG seqDebug("EQPacket: SEQStart found, setting arq seq, %04x stream %s", arqSeq, EQStreamStr[m_streamid]); #endif initDecode(); // hey, a SEQStart, use it's packet to set ARQ m_arqSeqExp = arqSeq; m_arqSeqFound = true; emit seqExpect(m_arqSeqExp, (int)m_streamid); if ((m_streamid == zone2client) && m_session_tracking_enabled) { m_session_tracking_enabled = 2; emit lockOnClient(packet.getSourcePort(), packet.getDestPort()); // notify that the client port has been latched emit sessionTrackingChanged(m_session_tracking_enabled); } } else if (!m_arqSeqFound && m_session_tracking_enabled == 0 && !packet.isClosingHi() && !packet.isClosingLo() && (m_streamid == zone2client)) { #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: new sequence found, setting arq seq, %04x stream %s", arqSeq, EQStreamStr[m_streamid]); #endif m_arqSeqExp = arqSeq; m_arqSeqFound = true; emit seqExpect(m_arqSeqExp, (int)m_streamid); } // is this the currently expected sequence, if so, do something with it. if (m_arqSeqExp == arqSeq) { m_arqSeqExp = arqSeq + 1; emit seqExpect(m_arqSeqExp, (int)m_streamid); #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: Found next arq in data stream %s, incrementing arq seq, %04x", EQStreamStr[m_streamid], arqSeq); #endif if (!packet.isASQ() && !packet.isFragment() && !packet.isClosingHi()) { // seems to be a sort of ping from client to server, has ARQ // but no ASQ, Flags look like 0x0201 (network byte order) #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: ARQ without ASQ from stream %s arq 0x%04x", EQStreamStr[m_streamid], arqSeq); #endif } // since the servers do not care about client closing sequences, we won't either // Hey clients have rights too, or not! else if (packet.isClosingHi() && packet.isClosingLo() && (m_streamid == zone2client)) { if (m_session_tracking_enabled) m_session_tracking_enabled = 1; emit closing(); return; } // if the packet is a fragment do appropriate processing else if (packet.isFragment()) processFragment(packet); else if (packet.payloadLength() >= 2) // has to have an opcode processPayload(packet.payload(), packet.payloadLength()); } // it's a packet from the future, add it to the cache else if ( ( (arqSeq > m_arqSeqExp) && (arqSeq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff))) ) || (arqSeq < (int32_t(m_arqSeqExp) - arqSeqWrapCutoff)) ) { #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order arq %04x stream %s, sending to cache, %04d", arqSeq, EQStreamStr[m_streamid], m_cache.size()); #endif setCache(arqSeq, packet); } // if the cache isn't empty, then check for the expected ARQ sequence if (!m_cache.empty()) processCache(); } /* end ARQ processing */ }
//////////////////////////////////////////////////// // Constructor EQPacket::EQPacket (QObject * parent, const char *name) : QObject (parent, name), m_packetCapture(NULL), m_vPacket(NULL), m_timer(NULL), m_busy_decoding(false) { // Setup the data streams // Setup client -> world stream m_client2WorldStream = new EQPacketStream(client2world, DIR_CLIENT, showeq_params->arqSeqGiveUp, this, "client2world"); connect(m_client2WorldStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2WorldStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2WorldStream, SIGNAL(dispatchData(const uint8_t*, size_t, uint8_t, uint16_t)), this, SLOT(dispatchWorldData(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2WorldStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_client2WorldStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_client2WorldStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_client2WorldStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Setup world -> client stream m_world2ClientStream = new EQPacketStream(world2client, DIR_SERVER, showeq_params->arqSeqGiveUp, this, "world2client"); connect(m_world2ClientStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_world2ClientStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_world2ClientStream, SIGNAL(dispatchData(const uint8_t*, size_t, uint8_t, uint16_t)), this, SLOT(dispatchWorldData(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_world2ClientStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_world2ClientStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_world2ClientStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_world2ClientStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Setup client -> zone stream m_client2ZoneStream = new EQPacketStream(client2zone, DIR_CLIENT, showeq_params->arqSeqGiveUp, this, "client2zone"); connect(m_client2ZoneStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2ZoneStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2ZoneStream, SIGNAL(dispatchData(const uint8_t*, size_t, uint8_t, uint16_t)), this, SLOT(dispatchZoneData(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2ZoneStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_client2ZoneStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_client2ZoneStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_client2ZoneStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Setup zone -> client stream m_zone2ClientStream = new EQPacketStream(zone2client, DIR_SERVER, showeq_params->arqSeqGiveUp, this, "zone2client"); connect(m_zone2ClientStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_zone2ClientStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_zone2ClientStream, SIGNAL(dispatchData(const uint8_t*, size_t, uint8_t, uint16_t)), this, SLOT(dispatchZoneData(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_zone2ClientStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_zone2ClientStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_zone2ClientStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_zone2ClientStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Zone to client stream specific signals (session tracking non-sense) connect(m_zone2ClientStream, SIGNAL(sessionTrackingChanged(uint8_t)), this, SIGNAL(sessionTrackingChanged(uint8_t))); connect(m_zone2ClientStream, SIGNAL(lockOnClient(in_port_t, in_port_t)), this, SLOT(lockOnClient(in_port_t, in_port_t))); connect(m_zone2ClientStream, SIGNAL(closing()), this, SLOT(closeStream())); // Initialize convenient streams array m_streams[client2world] = m_client2WorldStream; m_streams[world2client] = m_world2ClientStream; m_streams[client2zone] = m_client2ZoneStream; m_streams[zone2client] = m_zone2ClientStream; // no client/server ports yet m_clientPort = 0; m_serverPort = 0; struct hostent *he; struct in_addr ia; if (showeq_params->ip.isEmpty() && showeq_params->mac_address.isEmpty()) { printf ("No address specified\n"); exit(0); } if (!showeq_params->ip.isEmpty()) { /* Substitute "special" IP which is interpreted to set up a different filter for picking up new sessions */ if (showeq_params->ip == "auto") inet_aton (AUTOMATIC_CLIENT_IP, &ia); else if (inet_aton (showeq_params->ip, &ia) == 0) { he = gethostbyname(showeq_params->ip); if (!he) { printf ("Invalid address; %s\n", (const char*)showeq_params->ip); exit (0); } memcpy (&ia, he->h_addr_list[0], he->h_length); } m_client_addr = ia.s_addr; showeq_params->ip = inet_ntoa(ia); if (showeq_params->ip == AUTOMATIC_CLIENT_IP) { m_detectingClient = true; printf("Listening for first client seen.\n"); } else { m_detectingClient = false; printf("Listening for client: %s\n", (const char*)showeq_params->ip); } } if (!showeq_params->playbackpackets) { // create the pcap object and initialize, either with MAC or IP m_packetCapture = new PacketCaptureThread(); if (showeq_params->mac_address.length() == 17) m_packetCapture->start(showeq_params->device, showeq_params->mac_address, showeq_params->realtime, MAC_ADDRESS_TYPE ); else m_packetCapture->start(showeq_params->device, showeq_params->ip, showeq_params->realtime, IP_ADDRESS_TYPE ); emit filterChanged(); } /* Create timer object */ m_timer = new QTimer (this); if (!showeq_params->playbackpackets) connect (m_timer, SIGNAL (timeout ()), this, SLOT (processPackets ())); else connect (m_timer, SIGNAL (timeout ()), this, SLOT (processPlaybackPackets ())); /* setup VPacket */ m_vPacket = NULL; QString section = "VPacket"; // First param to VPacket is the filename // Second param is playback speed: 0 = fast as poss, 1 = 1X, 2 = 2X etc if (pSEQPrefs->isPreference("Filename", section)) { const char *filename = pSEQPrefs->getPrefString("Filename", section); if (showeq_params->recordpackets) { m_vPacket = new VPacket(filename, 1, true); // Must appear befire next call to getPrefString, which uses a static string printf("Recording packets to '%s' for future playback\n", filename); if (pSEQPrefs->getPrefString("FlushPackets", section)) m_vPacket->setFlushPacket(true); } else if (showeq_params->playbackpackets) { m_vPacket = new VPacket(filename, 1, false); m_vPacket->setCompressTime(pSEQPrefs->getPrefInt("CompressTime", section, 0)); m_vPacket->setPlaybackSpeed(showeq_params->playbackspeed); printf("Playing back packets from '%s' at speed '%d'\n", filename, showeq_params->playbackspeed); } } else { showeq_params->recordpackets = 0; showeq_params->playbackpackets = 0; } }
//////////////////////////////////////////////////// // Constructor EQPacket::EQPacket(const QString& worldopcodesxml, const QString& zoneopcodesxml, uint16_t arqSeqGiveUp, QString device, QString ip, QString mac_address, bool realtime, bool session_tracking, bool crcWarnings, bool recordPackets, bool playbackPackets, int8_t playbackSpeed, QObject * parent, const char *name) : QObject (parent, name), m_packetCapture(NULL), m_vPacket(NULL), m_timer(NULL), m_busy_decoding(false), m_arqSeqGiveUp(arqSeqGiveUp), m_device(device), m_ip(ip), m_mac(mac_address), m_realtime(realtime), m_session_tracking(session_tracking), m_crcWarnings(crcWarnings), m_recordPackets(recordPackets), m_playbackPackets(playbackPackets), m_playbackSpeed(playbackSpeed) { // create the packet type db m_packetTypeDB = new EQPacketTypeDB(); #ifdef PACKET_OPCODEDB_DIAG m_packetTypeDB->list(); #endif // create the world opcode db (with hash size of 29) m_worldOPCodeDB = new EQPacketOPCodeDB(29); // load the world opcode db if (!m_worldOPCodeDB->load(*m_packetTypeDB, worldopcodesxml)) seqFatal("Error loading '%s'!", (const char*)worldopcodesxml); #ifdef PACKET_OPCODEDB_DIAG m_worldOPCodeDB->list(); #endif //m_worldOPCodeDB->save("/tmp/worldopcodes.xml"); // create the zone opcode db (with hash size of 211) m_zoneOPCodeDB = new EQPacketOPCodeDB(211); // load the zone opcode db if (!m_zoneOPCodeDB->load(*m_packetTypeDB, zoneopcodesxml)) seqFatal("Error loading '%s'!", (const char*)zoneopcodesxml); #ifdef PACKET_OPCODEDB_DIAG m_zoneOPCodeDB->list(); #endif //m_zoneOPCodeDB->save("/tmp/zoneopcodes.xml"); // Setup the data streams // Setup client -> world stream m_client2WorldStream = new EQPacketStream(client2world, DIR_Client, m_arqSeqGiveUp, *m_worldOPCodeDB, this, "client2world"); connect(m_client2WorldStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2WorldStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), this, SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); connect(m_client2WorldStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_client2WorldStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_client2WorldStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_client2WorldStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Setup world -> client stream m_world2ClientStream = new EQPacketStream(world2client, DIR_Server, m_arqSeqGiveUp, *m_worldOPCodeDB, this, "world2client"); connect(m_world2ClientStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_world2ClientStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), this, SIGNAL(decodedWorldPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); connect(m_world2ClientStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_world2ClientStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_world2ClientStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_world2ClientStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Setup client -> zone stream m_client2ZoneStream = new EQPacketStream(client2zone, DIR_Client, m_arqSeqGiveUp, *m_zoneOPCodeDB, this, "client2zone"); connect(m_client2ZoneStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_client2ZoneStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); connect(m_client2ZoneStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*,bool)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*,bool))); connect(m_client2ZoneStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_client2ZoneStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_client2ZoneStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_client2ZoneStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Setup zone -> client stream m_zone2ClientStream = new EQPacketStream(zone2client, DIR_Server, m_arqSeqGiveUp, *m_zoneOPCodeDB, this, "zone2client"); connect(m_zone2ClientStream, SIGNAL(rawPacket(const uint8_t*, size_t, uint8_t, uint16_t)), this, SIGNAL(rawZonePacket(const uint8_t*, size_t, uint8_t, uint16_t))); connect(m_zone2ClientStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*))); connect(m_zone2ClientStream, SIGNAL(decodedPacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool)), this, SIGNAL(decodedZonePacket(const uint8_t*, size_t, uint8_t, uint16_t, const EQPacketOPCode*, bool))); connect(m_zone2ClientStream, SIGNAL(cacheSize(int, int)), this, SIGNAL(cacheSize(int, int))); connect(m_zone2ClientStream, SIGNAL(seqReceive(int, int)), this, SIGNAL(seqReceive(int, int))); connect(m_zone2ClientStream, SIGNAL(seqExpect(int, int)), this, SIGNAL(seqExpect(int, int))); connect(m_zone2ClientStream, SIGNAL(numPacket(int, int)), this, SIGNAL(numPacket(int, int))); // Zone to client stream specific signals (session tracking non-sense) connect(m_zone2ClientStream, SIGNAL(sessionTrackingChanged(uint8_t)), this, SIGNAL(sessionTrackingChanged(uint8_t))); connect(m_zone2ClientStream, SIGNAL(lockOnClient(in_port_t, in_port_t)), this, SLOT(lockOnClient(in_port_t, in_port_t))); connect(m_zone2ClientStream, SIGNAL(closing()), this, SLOT(closeStream())); // Initialize convenient streams array m_streams[client2world] = m_client2WorldStream; m_streams[world2client] = m_world2ClientStream; m_streams[client2zone] = m_client2ZoneStream; m_streams[zone2client] = m_zone2ClientStream; // no client/server ports yet m_clientPort = 0; m_serverPort = 0; struct hostent *he; struct in_addr ia; if (m_ip.isEmpty() && m_mac.isEmpty()) seqFatal("No address specified"); if (!m_ip.isEmpty()) { /* Substitute "special" IP which is interpreted to set up a different filter for picking up new sessions */ if (m_ip == "auto") inet_aton (AUTOMATIC_CLIENT_IP, &ia); else if (inet_aton (m_ip, &ia) == 0) { he = gethostbyname(m_ip); if (!he) seqFatal("Invalid address; %s", (const char*)m_ip); memcpy (&ia, he->h_addr_list[0], he->h_length); } m_client_addr = ia.s_addr; m_ip = inet_ntoa(ia); if (m_ip == AUTOMATIC_CLIENT_IP) { m_detectingClient = true; seqInfo("Listening for first client seen."); } else { m_detectingClient = false; seqInfo("Listening for client: %s", (const char*)m_ip); } } if (!m_playbackPackets) { // create the pcap object and initialize, either with MAC or IP m_packetCapture = new PacketCaptureThread(); if (m_mac.length() == 17) m_packetCapture->start(m_device, m_mac, m_realtime, MAC_ADDRESS_TYPE ); else m_packetCapture->start(m_device, m_ip, m_realtime, IP_ADDRESS_TYPE ); emit filterChanged(); } // if running setuid root, then give up root access, since the PacketCapture // is the only thing that needed it. if ((geteuid() == 0) && (getuid() != geteuid())) setuid(getuid()); /* Create timer object */ m_timer = new QTimer (this); if (!m_playbackPackets) connect (m_timer, SIGNAL (timeout ()), this, SLOT (processPackets ())); else connect (m_timer, SIGNAL (timeout ()), this, SLOT (processPlaybackPackets ())); /* setup VPacket */ m_vPacket = NULL; QString section = "VPacket"; // First param to VPacket is the filename // Second param is playback speed: 0 = fast as poss, 1 = 1X, 2 = 2X etc if (pSEQPrefs->isPreference("Filename", section)) { const char *filename = pSEQPrefs->getPrefString("Filename", section); if (m_recordPackets) { m_vPacket = new VPacket(filename, 1, true); // Must appear befire next call to getPrefString, which uses a static string seqInfo("Recording packets to '%s' for future playback", filename); if (pSEQPrefs->getPrefString("FlushPackets", section)) m_vPacket->setFlushPacket(true); } else if (m_playbackPackets) { m_vPacket = new VPacket(filename, 1, false); m_vPacket->setCompressTime(pSEQPrefs->getPrefInt("CompressTime", section, 0)); m_vPacket->setPlaybackSpeed(m_playbackSpeed); seqInfo("Playing back packets from '%s' at speed '%d'", filename, m_playbackSpeed); } } else { m_recordPackets = 0; m_playbackPackets = 0; } }
///////////////////////////////////////////////////// // Handle a protocol level packet. This could be either a top-level // EQUDPIPPacket or a subpacket that is just an EQProtocolPacket. Either way // we use net opcodes here. void EQPacketStream::processPacket(EQProtocolPacket& packet, bool isSubpacket) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("-->EQPacketStream::processPacket, subpacket=%s on stream %s (%d)", (isSubpacket ? "true" : "false"), EQStreamStr[m_streamid], m_streamid); #endif if (IS_APP_OPCODE(packet.getNetOpCode())) { // This is an app-opcode directly on the wire with no wrapping protocol // information. Weird, but whatever gets the stream read, right? dispatchPacket(packet.payload(), packet.payloadLength(), packet.getNetOpCode(), m_opcodeDB.find(packet.getNetOpCode())); return; } // Process the net opcode switch (packet.getNetOpCode()) { case OP_Combined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found combined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Rolled up multiple packets inside this packet. Need to unroll them // and process them individually. subpacket starts after the net opcode. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; // OpCode (in net order) uint16_t subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Opcode is next. Net opcode or app opcode? if (IS_NET_OPCODE(subOpCode)) { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled net opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Net opcode. false = copy. true = subpacket EQProtocolPacket spacket(subpacket, subpacketLength, false, true); processPacket(spacket, true); } else { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // App opcode. Dispatch it, skipping opcode. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); } subpacket += subpacketLength; } } break; case OP_AppCombined: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: found appcombined packet (net op: %04x, size %d) on stream %s (%d). Unrolling.", packet.getNetOpCode(), packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif // Multiple app op codes in the same packet. Need to unroll and dispatch // them. uint8_t* subpacket = packet.payload(); while (subpacket < packet.payload() + packet.payloadLength()) { // Length specified first on the wire. uint8_t subpacketLength = subpacket[0]; // Move past the length subpacket++; if (subpacketLength != 0xff) { // Dispatch app op code using given packet length. Net order! uint16_t subOpCode = *(uint16_t*)(subpacket); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", subpacketLength-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], subpacketLength-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += subpacketLength; } else { // If original length is 0xff, it means it is a long one. The length // is 2 bytes and next. uint16_t longOne = eqntohuint16(subpacket); // Move past the 2 byte length subpacket += 2; // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)subpacket; #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: unrolling length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne, EQStreamStr[m_streamid], m_streamid, subOpCode); seqDebug("EQPacket: processing unrolled app opcode, length %d bytes from combined packet on stream %s (%d). Opcode %04x", longOne-2, EQStreamStr[m_streamid], m_streamid, subOpCode); #endif // Dispatch, skipping op code. dispatchPacket(&subpacket[2], longOne-2, subOpCode, m_opcodeDB.find(subOpCode)); // Move ahead subpacket += longOne; } } } break; case OP_Packet: { // Normal unfragmented sequenced packet. uint16_t seq = eqntohuint16(packet.payload()); emit seqReceive(seq, (int)m_streamid); if (seq >= m_arqSeqExp) { // Future packet? if (seq == m_arqSeqExp) { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); // OpCode next. Net order for op codes. uint16_t subOpCode = *(uint16_t*)(&packet.payload()[2]); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x, sub opcode %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode(), subOpCode); #endif // App opcode or net opcode? if (IS_NET_OPCODE(subOpCode)) { // Net opcode. false = no copy. true = subpacket. EQProtocolPacket spacket(&packet.payload()[2], packet.payloadLength()-2, false, true); processPacket(spacket, true); } else { // App opcode. Dispatch, skipping seq and opcode. dispatchPacket(&packet.payload()[4], packet.payloadLength()-4, subOpCode, m_opcodeDB.find(subOpCode)); } } else if (seq < (uint32_t(m_arqSeqExp + arqSeqWrapCutoff)) || seq < (int32_t(m_arqSeqExp - arqSeqWrapCutoff))) { // Yeah, future packet. Push it on the packet cache. #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { // Past packet outside the cut off seqWarn("SEQ: received sequenced %spacket outside the bounds of reasonableness. Expecting seq=%04x got seq=%04x, reasonableness being %d in the future.", (isSubpacket ? "sub" : ""), m_arqSeqExp, seq, arqSeqWrapCutoff); } } else { // Spooky packet from the past. Boo! #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("discarding %spacket netopcode=%04x seq=%d size=%d on stream %s (%d). Packet is in the past. We've moved on.", (isSubpacket ? "sub" : ""), packet.getNetOpCode(), seq, packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif } } break; case OP_Oversized: { // Fragmented sequenced data packet. uint16_t seq = eqntohuint16(packet.payload()); emit seqReceive(seq, (int)m_streamid); if (seq >= m_arqSeqExp) { // Future packet? if (seq > m_arqSeqExp) { // Yeah, future packet. Push it on the packet cache. #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: out of order sequence %04x stream %s (%d) expecting %04x, sending to cache, %04d", seq, EQStreamStr[m_streamid], m_streamid, m_arqSeqExp, m_cache.size()); #endif setCache(seq, packet); } else { // Expected packet. m_arqSeqExp++; emit seqExpect(m_arqSeqExp, (int)m_streamid); #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("SEQ: Found next sequence number in data stream %s (%d), incrementing expected seq, %04x (op code %04x)", EQStreamStr[m_streamid], m_streamid, seq, packet.getNetOpCode()); #endif // Push the fragment on. m_fragment.addFragment(packet); if (m_fragment.isComplete()) { // OpCode from fragment. In network order. uint16_t fragOpCode = *(uint16_t*)(m_fragment.data()); #ifdef PACKET_PROCESS_DIAG seqDebug("SEQ: Completed oversized app packet on stream %s with seq %04x, total size %d opcode %04x", EQStreamStr[m_streamid], seq, m_fragment.size()-2, fragOpCode); #endif // dispatch fragment. Skip opcode. dispatchPacket(&m_fragment.data()[2], m_fragment.size()-2, fragOpCode, m_opcodeDB.find(fragOpCode)); m_fragment.reset(); } } } else { // Spooky packet from the past. Boo! #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 1) seqDebug("discarding packet netopcode=%04x seq=%d size=%d on stream %s (%d). Packet is in the past. We've moved on.", packet.getNetOpCode(), seq, packet.payloadLength(), EQStreamStr[m_streamid], m_streamid); #endif } } break; case OP_SessionRequest: { // Session request from client to server. #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif // Pull off session request information SessionRequestStruct* request = (SessionRequestStruct*) packet.payload(); m_sessionId = eqntohuint32((uint8_t*)&(request->sessionId)); m_maxLength = eqntohuint32((uint8_t*)&(request->maxLength)); #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionRequest sessionId %u maxLength %u, awaiting key for stream %s (%d)", m_sessionId, m_maxLength, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionRequest contents: unknown %u, sessionId %u, maxLength %u", eqntohuint32((uint8_t*)&(request->unknown0000)), m_sessionId, m_maxLength); #endif m_arqSeqExp = 0; m_arqSeqFound = true; } break; case OP_SessionResponse: { // Session response from server #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif // Pull off session response information SessionResponseStruct* response = (SessionResponseStruct*) packet.payload(); m_maxLength = eqntohuint32((uint8_t*)&(response->maxLength)); m_sessionKey = eqntohuint32((uint8_t*)&(response->key)); m_sessionId = eqntohuint32((uint8_t*)&(response->sessionId)); #if defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionResponse sessionId %u maxLength %u, key is %u for stream %s (%d)", m_sessionId, m_maxLength, m_sessionKey, EQStreamStr[m_streamid], m_streamid); #endif #if defined(PACKET_SESSION_DIAG) && (PACKET_SESSION_DIAG > 1) seqDebug("EQPacket: SessionResponse contents: sessionId %u, key %u, unknown %u, unknown %u, maxLength %u, unknown %u", m_sessionId, m_sessionKey, eqntohuint16((uint8_t*)&(response->unknown0008)), response->unknown0010, m_maxLength, eqntohuint32((uint8_t*) &(response->unknown0015))); #endif // Provide key to corresponding stream from this session/stream emit sessionKey(m_sessionId, m_streamid, m_sessionKey); m_arqSeqExp = 0; m_arqSeqFound = true; // Session tracking if (m_session_tracking_enabled) { // If this is the world server talking to us, reset session tracking if // it is on so we unlatch the client in case of getting kicked. if (m_streamid == world2client) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); } // If this is the zone server talking to us, close the latch and lock else if (m_streamid == zone2client) { // SessionResponse should always be an outer protocol packet, so // the EQProtocolPacket passed in can be cast back to // EQUDPIPPacketFormat, which we need to go to get access to the IP // headers! m_session_tracking_enabled = 2; emit lockOnClient(((EQUDPIPPacketFormat&) packet).getSourcePort(), ((EQUDPIPPacketFormat&) packet).getDestPort()); emit sessionTrackingChanged(m_session_tracking_enabled); } } } break; case OP_SessionDisconnect: { #if defined(PACKET_PROCESS_DIAG) || defined(PACKET_SESSION_DIAG) seqDebug("EQPacket: SessionDisconnect found, resetting expected seq, stream %s (%d) (session tracking %s)", EQStreamStr[m_streamid], m_streamid, (m_session_tracking_enabled == 2 ? "locked on" : (m_session_tracking_enabled == 1 ? "enabled" : "disabled"))); #endif m_arqSeqExp = 0; // Clear cache resetCache(); // Signal closing. Unlatch session tracking if it is on. if (m_session_tracking_enabled) { m_session_tracking_enabled = 1; emit sessionTrackingChanged(m_session_tracking_enabled); } emit closing(); } break; case OP_Ack: case OP_Resend: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on for net opcode %04x seq %04x, stream %s (%d)", packet.getNetOpCode(), eqntohuint16(packet.payload()), EQStreamStr[m_streamid], m_streamid); #endif } break; case OP_KeepAlive: case OP_SessionStatRequest: case OP_SessionStatResponse: { #if defined(PACKET_PROCESS_DIAG) && (PACKET_PROCESS_DIAG > 2) seqDebug("EQPacket: no-op on for net opcode %04x, stream %s (%d)", packet.getNetOpCode(), EQStreamStr[m_streamid], m_streamid); #endif } break; default : { seqWarn("EQPacket: Unhandled net opcode %04x, stream %s, size %d", packet.getNetOpCode(), EQStreamStr[m_streamid], packet.payloadLength()); } } }