From 86395f53634b3ef1ce76a7f1e5edfdb61f8ffd80 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Sat, 25 Oct 2014 15:24:26 -0700 Subject: Fix header ranking --- src/wire/packets.hpp | 582 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 582 insertions(+) create mode 100644 src/wire/packets.hpp (limited to 'src/wire/packets.hpp') diff --git a/src/wire/packets.hpp b/src/wire/packets.hpp new file mode 100644 index 0000000..a233344 --- /dev/null +++ b/src/wire/packets.hpp @@ -0,0 +1,582 @@ +#pragma once +// packets.hpp - palatable socket buffer accessors +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "fwd.hpp" + +#include + +#include "../ints/little.hpp" + +#include "../compat/cast.hpp" + +#include "../proto2/fwd.hpp" + +#include "../net/socket.hpp" + + +namespace tmwa +{ +struct Buffer +{ + std::vector bytes; +}; + +enum class RecvResult +{ + Incomplete, + Complete, + Error, +}; + +enum class SendResult +{ + Success, + Fail, +}; + + +size_t packet_avail(Session *s); +void packet_dump(io::WriteFile& out, Session *s); + +bool packet_fetch(Session *s, size_t offset, Byte *data, size_t sz); +void packet_discard(Session *s, size_t sz); +bool packet_send(Session *s, const Byte *data, size_t sz); + +inline +bool packet_peek_id(Session *s, uint16_t *packet_id) +{ + Little16 id; + bool okay = packet_fetch(s, 0, reinterpret_cast(&id), 2); + if (okay) + { + if (!network_to_native(packet_id, id)) + { + s->set_eof(); + return false; + } + } + return okay; +} + +inline +void send_buffer(Session *s, const Buffer& buffer) +{ + bool ok = !buffer.bytes.empty() && packet_send(s, buffer.bytes.data(), buffer.bytes.size()); + if (!ok) + s->set_eof(); +} + +template +__attribute__((warn_unused_result)) +RecvResult net_recv_fpacket(Session *s, NetPacket_Fixed& fixed) +{ + bool ok = packet_fetch(s, 0, reinterpret_cast(&fixed), sizeof(NetPacket_Fixed)); + if (ok) + { + packet_discard(s, sizeof(NetPacket_Fixed)); + return RecvResult::Complete; + } + return RecvResult::Incomplete; +} + +template +__attribute__((warn_unused_result)) +RecvResult net_recv_ppacket(Session *s, NetPacket_Payload& payload) +{ + bool ok = packet_fetch(s, 0, reinterpret_cast(&payload), sizeof(NetPacket_Payload)); + if (ok) + { + packet_discard(s, sizeof(NetPacket_Payload)); + return RecvResult::Complete; + } + return RecvResult::Incomplete; +} + +template +__attribute__((warn_unused_result)) +RecvResult net_recv_vpacket(Session *s, NetPacket_Head& head, std::vector>& repeat) +{ + bool ok = packet_fetch(s, 0, reinterpret_cast(&head), sizeof(NetPacket_Head)); + if (ok) + { + Packet_Head nat; + if (!network_to_native(&nat, head)) + return RecvResult::Error; + if (packet_avail(s) < nat.magic_packet_length) + return RecvResult::Incomplete; + if (nat.magic_packet_length < sizeof(NetPacket_Head)) + return RecvResult::Error; + size_t bytes_repeat = nat.magic_packet_length - sizeof(NetPacket_Head); + if (bytes_repeat % sizeof(NetPacket_Repeat)) + return RecvResult::Error; + repeat.resize(bytes_repeat / sizeof(NetPacket_Repeat)); + if (packet_fetch(s, sizeof(NetPacket_Head), reinterpret_cast(repeat.data()), bytes_repeat)) + { + packet_discard(s, nat.magic_packet_length); + return RecvResult::Complete; + } + return RecvResult::Incomplete; + } + return RecvResult::Incomplete; +} + +template +__attribute__((warn_unused_result)) +RecvResult net_recv_opacket(Session *s, NetPacket_Head& head, bool *has_opt, NetPacket_Option& opt) +{ + bool ok = packet_fetch(s, 0, reinterpret_cast(&head), sizeof(NetPacket_Head)); + if (ok) + { + Packet_Head nat; + if (!network_to_native(&nat, head)) + return RecvResult::Error; + if (packet_avail(s) < nat.magic_packet_length) + return RecvResult::Incomplete; + if (nat.magic_packet_length < sizeof(NetPacket_Head)) + return RecvResult::Error; + size_t bytes_repeat = nat.magic_packet_length - sizeof(NetPacket_Head); + if (bytes_repeat % sizeof(NetPacket_Option)) + return RecvResult::Error; + size_t has_opt_pls = bytes_repeat / sizeof(NetPacket_Option); + if (has_opt_pls > 1) + return RecvResult::Error; + *has_opt = has_opt_pls; + if (!*has_opt || packet_fetch(s, sizeof(NetPacket_Head), reinterpret_cast(&opt), sizeof(NetPacket_Option))) + { + packet_discard(s, nat.magic_packet_length); + return RecvResult::Complete; + } + return RecvResult::Incomplete; + } + return RecvResult::Incomplete; +} + + +template +Buffer create_fpacket(const Packet_Fixed& fixed) +{ + static_assert(id == Packet_Fixed::PACKET_ID, "Packet_Fixed::PACKET_ID"); + static_assert(size == sizeof(NetPacket_Fixed), "sizeof(NetPacket_Fixed)"); + + Buffer buf; + buf.bytes.resize(sizeof(NetPacket_Fixed)); + auto& net_fixed = reinterpret_cast&>( + *(buf.bytes.begin() + 0)); + if (!native_to_network(&net_fixed, fixed)) + { + return Buffer(); + } + return buf; +} + +template +Buffer create_ppacket(Packet_Payload& payload) +{ + static_assert(id == Packet_Payload::PACKET_ID, "Packet_Payload::PACKET_ID"); + + if (id != 0x8000) + payload.magic_packet_length = sizeof(NetPacket_Payload); + + Buffer buf; + buf.bytes.resize(sizeof(NetPacket_Payload)); + auto& net_payload = reinterpret_cast&>( + *(buf.bytes.begin() + 0)); + if (!native_to_network(&net_payload, payload)) + { + return Buffer(); + } + return buf; +} + +template +Buffer create_vpacket(Packet_Head& head, const std::vector>& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "sizeof(NetPacket_Head)"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + + // since these are already allocated, can't overflow address space + size_t total_size = sizeof(NetPacket_Head) + repeat.size() * sizeof(NetPacket_Repeat); + // truncates + head.magic_packet_length = total_size; + if (head.magic_packet_length != total_size) + { + return Buffer(); + } + + Buffer buf; + buf.bytes.resize(total_size); + auto& net_head = reinterpret_cast&>( + *(buf.bytes.begin() + 0)); + if (!native_to_network(&net_head, head)) + { + return Buffer(); + } + for (size_t i = 0; i < repeat.size(); ++i) + { + auto& net_repeat_i = reinterpret_cast&>( + *(buf.bytes.begin() + + sizeof(NetPacket_Head) + + i * sizeof(NetPacket_Repeat))); + if (!native_to_network(&net_repeat_i, repeat[i])) + { + return Buffer(); + } + } + return buf; +} + +template +Buffer create_opacket(Packet_Head& head, bool has_opt, const Packet_Option& opt) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "sizeof(NetPacket_Head)"); + static_assert(id == Packet_Option::PACKET_ID, "Packet_Option::PACKET_ID"); + static_assert(optsize == sizeof(NetPacket_Option), "sizeof(NetPacket_Option)"); + + // since these are already allocated, can't overflow address space + size_t total_size = sizeof(NetPacket_Head) + has_opt * sizeof(NetPacket_Option); + // truncates + head.magic_packet_length = total_size; + if (head.magic_packet_length != total_size) + { + return Buffer(); + } + + Buffer buf; + buf.bytes.resize(total_size); + + auto& net_head = reinterpret_cast&>( + *(buf.bytes.begin() + 0)); + if (!native_to_network(&net_head, head)) + { + return Buffer(); + } + if (has_opt) + { + auto& net_opt = reinterpret_cast&>( + *(buf.bytes.begin() + + sizeof(NetPacket_Head))); + if (!native_to_network(&net_opt, opt)) + { + return Buffer(); + } + } + + return buf; +} + +template +void send_fpacket(Session *s, const Packet_Fixed& fixed) +{ + Buffer pkt = create_fpacket(fixed); + send_buffer(s, pkt); +} + +template +void send_ppacket(Session *s, Packet_Payload& payload) +{ + Buffer pkt = create_ppacket(payload); + send_buffer(s, pkt); +} + +template +void send_vpacket(Session *s, Packet_Head& head, const std::vector>& repeat) +{ + Buffer pkt = create_vpacket(head, repeat); + send_buffer(s, pkt); +} + +template +void send_opacket(Session *s, Packet_Head& head, bool has_opt, const Packet_Option& opt) +{ + Buffer pkt = create_opacket(head, has_opt, opt); + send_buffer(s, pkt); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_fpacket(Session *s, Packet_Fixed& fixed) +{ + static_assert(id == Packet_Fixed::PACKET_ID, "Packet_Fixed::PACKET_ID"); + static_assert(size == sizeof(NetPacket_Fixed), "NetPacket_Fixed"); + + NetPacket_Fixed net_fixed; + RecvResult rv = net_recv_fpacket(s, net_fixed); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&fixed, net_fixed)) + return RecvResult::Error; + assert (fixed.magic_packet_id == Packet_Fixed::PACKET_ID); + } + return rv; +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_ppacket(Session *s, Packet_Payload& payload) +{ + static_assert(id == Packet_Payload::PACKET_ID, "Packet_Payload::PACKET_ID"); + + NetPacket_Payload net_payload; + RecvResult rv = net_recv_ppacket(s, net_payload); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&payload, net_payload)) + return RecvResult::Error; + assert (payload.magic_packet_id == Packet_Payload::PACKET_ID); + if (id == 0x8000) + { + // 0x8000 is special + if (packet_avail(s) < payload.magic_packet_length) + return RecvResult::Incomplete; + payload.magic_packet_length = 4; + return RecvResult::Complete; + } + if (payload.magic_packet_length != sizeof(net_payload)) + return RecvResult::Error; + } + return rv; +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_vpacket(Session *s, Packet_Head& head, std::vector>& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "NetPacket_Head"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "NetPacket_Repeat"); + + NetPacket_Head net_head; + std::vector> net_repeat; + RecvResult rv = net_recv_vpacket(s, net_head, net_repeat); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&head, net_head)) + return RecvResult::Error; + assert (head.magic_packet_id == Packet_Head::PACKET_ID); + + repeat.resize(net_repeat.size()); + for (size_t i = 0; i < net_repeat.size(); ++i) + { + if (!network_to_native(&repeat[i], net_repeat[i])) + return RecvResult::Error; + } + } + return rv; +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_opacket(Session *s, Packet_Head& head, bool *has_opt, Packet_Option& opt) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "NetPacket_Head"); + static_assert(id == Packet_Option::PACKET_ID, "Packet_Option::PACKET_ID"); + static_assert(optsize == sizeof(NetPacket_Option), "NetPacket_Option"); + + NetPacket_Head net_head; + NetPacket_Option net_opt; + RecvResult rv = net_recv_opacket(s, net_head, has_opt, net_opt); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&head, net_head)) + return RecvResult::Error; + assert (head.magic_packet_id == Packet_Head::PACKET_ID); + + if (*has_opt) + { + if (!network_to_native(&opt, net_opt)) + return RecvResult::Error; + } + } + return rv; +} + + +// convenience for trailing strings + +template +Buffer create_vpacket(Packet_Head& head, const XString& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "NetPacket_Head"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "NetPacket_Repeat"); + static_assert(repeatsize == 1, "repeatsize"); + + // since it's already allocated, it can't overflow address space + size_t total_length = sizeof(NetPacket_Head) + (repeat.size() + 1) * sizeof(NetPacket_Repeat); + head.magic_packet_length = total_length; + if (head.magic_packet_length != total_length) + { + return Buffer(); + } + + Buffer buf; + buf.bytes.resize(total_length); + auto& net_head = reinterpret_cast&>( + *(buf.bytes.begin() + 0)); + std::vector> net_repeat(repeat.size() + 1); + if (!native_to_network(&net_head, head)) + { + return Buffer(); + } + for (size_t i = 0; i < repeat.size(); ++i) + { + auto& net_repeat_i = reinterpret_cast&>( + *(buf.bytes.begin() + + sizeof(NetPacket_Head) + + i)); + net_repeat_i.c = Byte{static_cast(repeat[i])}; + } + auto& net_repeat_repeat_size = reinterpret_cast&>( + *(buf.bytes.begin() + + sizeof(NetPacket_Head) + + repeat.size())); + net_repeat_repeat_size.c = Byte{static_cast('\0')}; + return buf; +} + +template +void send_vpacket(Session *s, Packet_Head& head, const XString& repeat) +{ + Buffer pkt = create_vpacket(head, repeat); + send_buffer(s, pkt); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_vpacket(Session *s, Packet_Head& head, AString& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "NetPacket_Head"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "NetPacket_Repeat"); + static_assert(repeatsize == 1, "repeatsize"); + + NetPacket_Head net_head; + std::vector> net_repeat; + RecvResult rv = net_recv_vpacket(s, net_head, net_repeat); + assert (head.magic_packet_id == Packet_Head::PACKET_ID); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&head, net_head)) + return RecvResult::Error; + // reinterpret_cast is needed to correctly handle an empty vector + const char *begin = sign_cast(net_repeat.data()); + const char *end = begin + net_repeat.size(); + end = std::find(begin, end, '\0'); + repeat = XString(begin, end, nullptr); + } + return rv; +} + + +// if there is nothing in the head but the id and length, use the below + +template +Buffer create_packet_repeatonly(const std::vector>& v) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "repeat headsize"); + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + + Packet_Head head; + return create_vpacket(head, v); +} + +template +void send_packet_repeatonly(Session *s, const std::vector>& v) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "repeat headsize"); + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + + Packet_Head head; + send_vpacket(s, head, v); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_packet_repeatonly(Session *s, std::vector>& v) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "repeat headsize"); + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + + Packet_Head head; + return recv_vpacket(s, head, v); +} + + +// and the combination of both of the above + +template +Buffer create_packet_repeatonly(const XString& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "repeat headsize"); + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + static_assert(repeatsize == 1, "repeatsize"); + + Packet_Head head; + return create_vpacket(head, repeat); +} + +template +void send_packet_repeatonly(Session *s, const XString& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "repeat headsize"); + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + static_assert(repeatsize == 1, "repeatsize"); + + Packet_Head head; + send_vpacket(s, head, repeat); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_packet_repeatonly(Session *s, AString& repeat) +{ + static_assert(id == Packet_Head::PACKET_ID, "Packet_Head::PACKET_ID"); + static_assert(headsize == sizeof(NetPacket_Head), "repeat headsize"); + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == Packet_Repeat::PACKET_ID, "Packet_Repeat::PACKET_ID"); + static_assert(repeatsize == sizeof(NetPacket_Repeat), "sizeof(NetPacket_Repeat)"); + static_assert(repeatsize == 1, "repeatsize"); + + Packet_Head head; + return recv_vpacket(s, head, repeat); +} +} // namespace tmwa -- cgit v1.2.3-60-g2f50