#pragma once // packets.hpp - palatable socket buffer accessors // // Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> // // 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 <http://www.gnu.org/licenses/>. #include "fwd.hpp" #include <vector> #include "../ints/little.hpp" #include "../compat/cast.hpp" #include "../proto2/fwd.hpp" #include "../net/socket.hpp" namespace tmwa { struct Buffer { std::vector<Byte> bytes; }; enum class RecvResult { Incomplete, Complete, Error, }; enum class SendResult { Success, Fail, }; size_t packet_avail(Session *s); void packet_dump(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<Byte *>(&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<uint16_t id> __attribute__((warn_unused_result)) RecvResult net_recv_fpacket(Session *s, NetPacket_Fixed<id>& fixed) { bool ok = packet_fetch(s, 0, reinterpret_cast<Byte *>(&fixed), sizeof(NetPacket_Fixed<id>)); if (ok) { packet_discard(s, sizeof(NetPacket_Fixed<id>)); return RecvResult::Complete; } return RecvResult::Incomplete; } template<uint16_t id> __attribute__((warn_unused_result)) RecvResult net_recv_ppacket(Session *s, NetPacket_Payload<id>& payload) { bool ok = packet_fetch(s, 0, reinterpret_cast<Byte *>(&payload), sizeof(NetPacket_Payload<id>)); if (ok) { packet_discard(s, sizeof(NetPacket_Payload<id>)); return RecvResult::Complete; } return RecvResult::Incomplete; } template<uint16_t id> __attribute__((warn_unused_result)) RecvResult net_recv_vpacket(Session *s, NetPacket_Head<id>& head, std::vector<NetPacket_Repeat<id>>& repeat) { bool ok = packet_fetch(s, 0, reinterpret_cast<Byte *>(&head), sizeof(NetPacket_Head<id>)); if (ok) { Packet_Head<id> 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<id>)) return RecvResult::Error; size_t bytes_repeat = nat.magic_packet_length - sizeof(NetPacket_Head<id>); if (bytes_repeat % sizeof(NetPacket_Repeat<id>)) return RecvResult::Error; repeat.resize(bytes_repeat / sizeof(NetPacket_Repeat<id>)); if (packet_fetch(s, sizeof(NetPacket_Head<id>), reinterpret_cast<Byte *>(repeat.data()), bytes_repeat)) { packet_discard(s, nat.magic_packet_length); return RecvResult::Complete; } return RecvResult::Incomplete; } return RecvResult::Incomplete; } template<uint16_t id> __attribute__((warn_unused_result)) RecvResult net_recv_opacket(Session *s, NetPacket_Head<id>& head, bool *has_opt, NetPacket_Option<id>& opt) { bool ok = packet_fetch(s, 0, reinterpret_cast<Byte *>(&head), sizeof(NetPacket_Head<id>)); if (ok) { Packet_Head<id> 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<id>)) return RecvResult::Error; size_t bytes_repeat = nat.magic_packet_length - sizeof(NetPacket_Head<id>); if (bytes_repeat % sizeof(NetPacket_Option<id>)) return RecvResult::Error; size_t has_opt_pls = bytes_repeat / sizeof(NetPacket_Option<id>); if (has_opt_pls > 1) return RecvResult::Error; *has_opt = has_opt_pls; if (!*has_opt || packet_fetch(s, sizeof(NetPacket_Head<id>), reinterpret_cast<Byte *>(&opt), sizeof(NetPacket_Option<id>))) { packet_discard(s, nat.magic_packet_length); return RecvResult::Complete; } return RecvResult::Incomplete; } return RecvResult::Incomplete; } template<uint16_t id, uint16_t size> Buffer create_fpacket(const Packet_Fixed<id>& fixed) { static_assert(id == Packet_Fixed<id>::PACKET_ID, "Packet_Fixed<id>::PACKET_ID"); static_assert(size == sizeof(NetPacket_Fixed<id>), "sizeof(NetPacket_Fixed<id>)"); Buffer buf; buf.bytes.resize(sizeof(NetPacket_Fixed<id>)); auto& net_fixed = reinterpret_cast<NetPacket_Fixed<id>&>( *(buf.bytes.begin() + 0)); if (!native_to_network(&net_fixed, fixed)) { return Buffer(); } return buf; } template<uint16_t id> Buffer create_ppacket(Packet_Payload<id>& payload) { static_assert(id == Packet_Payload<id>::PACKET_ID, "Packet_Payload<id>::PACKET_ID"); if (id != 0x8000) payload.magic_packet_length = sizeof(NetPacket_Payload<id>); Buffer buf; buf.bytes.resize(sizeof(NetPacket_Payload<id>)); auto& net_payload = reinterpret_cast<NetPacket_Payload<id>&>( *(buf.bytes.begin() + 0)); if (!native_to_network(&net_payload, payload)) { return Buffer(); } return buf; } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> Buffer create_vpacket(Packet_Head<id>& head, const std::vector<Packet_Repeat<id>>& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "sizeof(NetPacket_Head<id>)"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); // since these are already allocated, can't overflow address space size_t total_size = sizeof(NetPacket_Head<id>) + repeat.size() * sizeof(NetPacket_Repeat<id>); // 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<NetPacket_Head<id>&>( *(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<NetPacket_Repeat<id>&>( *(buf.bytes.begin() + sizeof(NetPacket_Head<id>) + i * sizeof(NetPacket_Repeat<id>))); if (!native_to_network(&net_repeat_i, repeat[i])) { return Buffer(); } } return buf; } template<uint16_t id, uint16_t headsize, uint16_t optsize> Buffer create_opacket(Packet_Head<id>& head, bool has_opt, const Packet_Option<id>& opt) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "sizeof(NetPacket_Head<id>)"); static_assert(id == Packet_Option<id>::PACKET_ID, "Packet_Option<id>::PACKET_ID"); static_assert(optsize == sizeof(NetPacket_Option<id>), "sizeof(NetPacket_Option<id>)"); // since these are already allocated, can't overflow address space size_t total_size = sizeof(NetPacket_Head<id>) + has_opt * sizeof(NetPacket_Option<id>); // 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<NetPacket_Head<id>&>( *(buf.bytes.begin() + 0)); if (!native_to_network(&net_head, head)) { return Buffer(); } if (has_opt) { auto& net_opt = reinterpret_cast<NetPacket_Option<id>&>( *(buf.bytes.begin() + sizeof(NetPacket_Head<id>))); if (!native_to_network(&net_opt, opt)) { return Buffer(); } } return buf; } template<uint16_t id, uint16_t size> void send_fpacket(Session *s, const Packet_Fixed<id>& fixed) { Buffer pkt = create_fpacket<id, size>(fixed); send_buffer(s, pkt); } template<uint16_t id> void send_ppacket(Session *s, Packet_Payload<id>& payload) { Buffer pkt = create_ppacket<id>(payload); send_buffer(s, pkt); } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> void send_vpacket(Session *s, Packet_Head<id>& head, const std::vector<Packet_Repeat<id>>& repeat) { Buffer pkt = create_vpacket<id, headsize, repeatsize>(head, repeat); send_buffer(s, pkt); } template<uint16_t id, uint16_t headsize, uint16_t optsize> void send_opacket(Session *s, Packet_Head<id>& head, bool has_opt, const Packet_Option<id>& opt) { Buffer pkt = create_opacket<id, headsize, optsize>(head, has_opt, opt); send_buffer(s, pkt); } template<uint16_t id, uint16_t size> __attribute__((warn_unused_result)) RecvResult recv_fpacket(Session *s, Packet_Fixed<id>& fixed) { static_assert(id == Packet_Fixed<id>::PACKET_ID, "Packet_Fixed<id>::PACKET_ID"); static_assert(size == sizeof(NetPacket_Fixed<id>), "NetPacket_Fixed<id>"); NetPacket_Fixed<id> 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<id>::PACKET_ID); } return rv; } template<uint16_t id> __attribute__((warn_unused_result)) RecvResult recv_ppacket(Session *s, Packet_Payload<id>& payload) { static_assert(id == Packet_Payload<id>::PACKET_ID, "Packet_Payload<id>::PACKET_ID"); NetPacket_Payload<id> 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<id>::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<uint16_t id, uint16_t headsize, uint16_t repeatsize> __attribute__((warn_unused_result)) RecvResult recv_vpacket(Session *s, Packet_Head<id>& head, std::vector<Packet_Repeat<id>>& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "NetPacket_Head<id>"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "NetPacket_Repeat<id>"); NetPacket_Head<id> net_head; std::vector<NetPacket_Repeat<id>> 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<id>::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<uint16_t id, uint16_t headsize, uint16_t optsize> __attribute__((warn_unused_result)) RecvResult recv_opacket(Session *s, Packet_Head<id>& head, bool *has_opt, Packet_Option<id>& opt) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "NetPacket_Head<id>"); static_assert(id == Packet_Option<id>::PACKET_ID, "Packet_Option<id>::PACKET_ID"); static_assert(optsize == sizeof(NetPacket_Option<id>), "NetPacket_Option<id>"); NetPacket_Head<id> net_head; NetPacket_Option<id> 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<id>::PACKET_ID); if (*has_opt) { if (!network_to_native(&opt, net_opt)) return RecvResult::Error; } } return rv; } // convenience for trailing strings template<uint16_t id, uint16_t headsize, uint16_t repeatsize> Buffer create_vpacket(Packet_Head<id>& head, const XString& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "NetPacket_Head<id>"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "NetPacket_Repeat<id>"); static_assert(repeatsize == 1, "repeatsize"); // since it's already allocated, it can't overflow address space size_t total_length = sizeof(NetPacket_Head<id>) + (repeat.size() + 1) * sizeof(NetPacket_Repeat<id>); 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<NetPacket_Head<id>&>( *(buf.bytes.begin() + 0)); std::vector<NetPacket_Repeat<id>> 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<NetPacket_Repeat<id>&>( *(buf.bytes.begin() + sizeof(NetPacket_Head<id>) + i)); net_repeat_i.c = Byte{static_cast<uint8_t>(repeat[i])}; } auto& net_repeat_repeat_size = reinterpret_cast<NetPacket_Repeat<id>&>( *(buf.bytes.begin() + sizeof(NetPacket_Head<id>) + repeat.size())); net_repeat_repeat_size.c = Byte{static_cast<uint8_t>('\0')}; return buf; } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> void send_vpacket(Session *s, Packet_Head<id>& head, const XString& repeat) { Buffer pkt = create_vpacket<id, headsize, repeatsize>(head, repeat); send_buffer(s, pkt); } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> __attribute__((warn_unused_result)) RecvResult recv_vpacket(Session *s, Packet_Head<id>& head, AString& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "NetPacket_Head<id>"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "NetPacket_Repeat<id>"); static_assert(repeatsize == 1, "repeatsize"); NetPacket_Head<id> net_head; std::vector<NetPacket_Repeat<id>> net_repeat; RecvResult rv = net_recv_vpacket(s, net_head, net_repeat); assert (head.magic_packet_id == Packet_Head<id>::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<const char *>(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<uint16_t id, uint16_t headsize, uint16_t repeatsize> Buffer create_packet_repeatonly(const std::vector<Packet_Repeat<id>>& v) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "repeat headsize"); static_assert(headsize == 4, "repeat headsize"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); Packet_Head<id> head; return create_vpacket<id, 4, repeatsize>(head, v); } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> void send_packet_repeatonly(Session *s, const std::vector<Packet_Repeat<id>>& v) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "repeat headsize"); static_assert(headsize == 4, "repeat headsize"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); Packet_Head<id> head; send_vpacket<id, 4, repeatsize>(s, head, v); } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> __attribute__((warn_unused_result)) RecvResult recv_packet_repeatonly(Session *s, std::vector<Packet_Repeat<id>>& v) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "repeat headsize"); static_assert(headsize == 4, "repeat headsize"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); Packet_Head<id> head; return recv_vpacket<id, 4, repeatsize>(s, head, v); } // and the combination of both of the above template<uint16_t id, uint16_t headsize, uint16_t repeatsize> Buffer create_packet_repeatonly(const XString& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "repeat headsize"); static_assert(headsize == 4, "repeat headsize"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); static_assert(repeatsize == 1, "repeatsize"); Packet_Head<id> head; return create_vpacket<id, 4, repeatsize>(head, repeat); } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> void send_packet_repeatonly(Session *s, const XString& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "repeat headsize"); static_assert(headsize == 4, "repeat headsize"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); static_assert(repeatsize == 1, "repeatsize"); Packet_Head<id> head; send_vpacket<id, 4, repeatsize>(s, head, repeat); } template<uint16_t id, uint16_t headsize, uint16_t repeatsize> __attribute__((warn_unused_result)) RecvResult recv_packet_repeatonly(Session *s, AString& repeat) { static_assert(id == Packet_Head<id>::PACKET_ID, "Packet_Head<id>::PACKET_ID"); static_assert(headsize == sizeof(NetPacket_Head<id>), "repeat headsize"); static_assert(headsize == 4, "repeat headsize"); static_assert(id == Packet_Repeat<id>::PACKET_ID, "Packet_Repeat<id>::PACKET_ID"); static_assert(repeatsize == sizeof(NetPacket_Repeat<id>), "sizeof(NetPacket_Repeat<id>)"); static_assert(repeatsize == 1, "repeatsize"); Packet_Head<id> head; return recv_vpacket<id, 4, repeatsize>(s, head, repeat); } } // namespace tmwa