From 2410aeb608329ed57a31315effdfd5a751788616 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Sun, 18 May 2014 02:43:55 -0700 Subject: Convert login/char and login/admin server components to proto-v2 --- src/net/packets.cpp | 89 +++++++++++++ src/net/packets.hpp | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/net/socket.cpp | 1 + src/net/socket.hpp | 13 -- src/net/vomit.hpp | 17 +++ 5 files changed, 461 insertions(+), 13 deletions(-) create mode 100644 src/net/packets.cpp create mode 100644 src/net/packets.hpp (limited to 'src/net') diff --git a/src/net/packets.cpp b/src/net/packets.cpp new file mode 100644 index 0000000..5595b1d --- /dev/null +++ b/src/net/packets.cpp @@ -0,0 +1,89 @@ +#include "packets.hpp" +// packets.cpp - 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 "../io/cxxstdio.hpp" +# include "../io/write.hpp" + +#include "vomit.hpp" + +#include "../poison.hpp" + +size_t packet_avail(Session *s) +{ + return RFIFOREST(s); +} + +bool packet_fetch(Session *s, size_t offset, Byte *data, size_t sz) +{ + if (RFIFOREST(s) < offset + sz) + return false; + const Byte *start = reinterpret_cast(RFIFOP(s, offset)); + const Byte *end = start + sz; + std::copy(start, end, data); + return true; +} +void packet_discard(Session *s, size_t sz) +{ + RFIFOSKIP(s, sz); +} +bool packet_send(Session *s, const Byte *data, size_t sz) +{ + WFIFOSET(s, sz); + Byte *end = reinterpret_cast(WFIFOP(s, 0)); + Byte *start = end - sz; + std::copy(data, data + sz, start); + return true; +} + +void packet_dump(io::WriteFile& logfp, Session *s) +{ + FPRINTF(logfp, + "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F\n"_fmt); + char tmpstr[16 + 1] {}; + int i; + for (i = 0; i < RFIFOREST(s); i++) + { + if ((i & 15) == 0) + FPRINTF(logfp, "%04X "_fmt, i); + FPRINTF(logfp, "%02x "_fmt, RFIFOB(s, i)); + if (RFIFOB(s, i) > 0x1f) + tmpstr[i % 16] = RFIFOB(s, i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + FPRINTF(logfp, " "_fmt); + else if ((i + 1) % 16 == 0) + { + FPRINTF(logfp, " %s\n"_fmt, tmpstr); + std::fill(tmpstr + 0, tmpstr + 17, '\0'); + } + } + if (i % 16 != 0) + { + for (int j = i; j % 16 != 0; j++) + { + FPRINTF(logfp, " "_fmt); + if ((j - 7) % 16 == 0) // -8 + 1 + FPRINTF(logfp, " "_fmt); + } + FPRINTF(logfp, " %s\n"_fmt, tmpstr); + } + FPRINTF(logfp, "\n"_fmt); +} diff --git a/src/net/packets.hpp b/src/net/packets.hpp new file mode 100644 index 0000000..2e3d77c --- /dev/null +++ b/src/net/packets.hpp @@ -0,0 +1,354 @@ +#ifndef TMWA_NET_PACKETS_HPP +#define TMWA_NET_PACKETS_HPP +// 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 "../compat/cast.hpp" + +# include "../ints/little.hpp" + + #include "../io/fwd.hpp" + +# include "socket.hpp" + +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; +} + +template +__attribute__((warn_unused_result)) +SendResult net_send_fpacket(Session *s, const F& fixed) +{ + bool ok = packet_send(s, reinterpret_cast(&fixed), sizeof(F)); + return ok ? SendResult::Success : SendResult::Fail; +} + +template +__attribute__((warn_unused_result)) +SendResult net_send_vpacket(Session *s, const H& head, const std::vector& repeat) +{ + bool ok = packet_send(s, reinterpret_cast(&head), sizeof(H)); + ok &= packet_send(s, reinterpret_cast(repeat.data()), repeat.size() * sizeof(R)); + return ok ? SendResult::Success : SendResult::Fail; +} + +template +__attribute__((warn_unused_result)) +RecvResult net_recv_fpacket(Session *s, F& fixed) +{ + bool ok = packet_fetch(s, 0, reinterpret_cast(&fixed), sizeof(F)); + if (ok) + { + packet_discard(s, sizeof(F)); + return RecvResult::Complete; + } + return RecvResult::Incomplete; +} + +template +__attribute__((warn_unused_result)) +RecvResult net_recv_vpacket(Session *s, H& head, std::vector& repeat) +{ + bool ok = packet_fetch(s, 0, reinterpret_cast(&head), sizeof(H)); + if (ok) + { + HNat 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(H)) + return RecvResult::Error; + size_t bytes_repeat = nat.magic_packet_length - sizeof(H); + if (bytes_repeat % sizeof(R)) + return RecvResult::Error; + repeat.resize(bytes_repeat / sizeof(R)); + if (packet_fetch(s, sizeof(H), reinterpret_cast(repeat.data()), bytes_repeat)) + { + packet_discard(s, nat.magic_packet_length); + return RecvResult::Complete; + } + return RecvResult::Incomplete; + } + return RecvResult::Incomplete; +} + + +template +void send_fpacket(Session *s, const F& fixed) +{ + static_assert(id == F::PACKET_ID, "F::PACKET_ID"); + static_assert(size == sizeof(typename F::NetType), "F::NetType"); + + typename F::NetType net_fixed; + if (!native_to_network(&net_fixed, fixed)) + { + s->set_eof(); + return; + } + SendResult rv = net_send_fpacket(s, net_fixed); + if (rv != SendResult::Success) + s->set_eof(); +} + +template +void send_vpacket(Session *s, H& head, const std::vector& repeat) +{ + static_assert(id == H::PACKET_ID, "H::PACKET_ID"); + static_assert(headsize == sizeof(typename H::NetType), "H::NetType"); + static_assert(id == R::PACKET_ID, "R::PACKET_ID"); + static_assert(repeatsize == sizeof(typename R::NetType), "R::NetType"); + + typename H::NetType net_head; + // since these are already allocated, can't overflow address space + size_t total_size = sizeof(typename H::NetType) + repeat.size() * sizeof(typename R::NetType); + // truncates + head.magic_packet_length = total_size; + if (head.magic_packet_length != total_size) + { + s->set_eof(); + return; + } + // TODO potentially avoid the allocation + std::vector net_repeat(repeat.size()); + if (!native_to_network(&net_head, head)) + { + s->set_eof(); + return; + } + for (size_t i = 0; i < repeat.size(); ++i) + { + if (!native_to_network(&net_repeat[i], repeat[i])) + { + s->set_eof(); + return; + } + } + SendResult rv = net_send_vpacket(s, net_head, net_repeat); + if (rv != SendResult::Success) + s->set_eof(); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_fpacket(Session *s, F& fixed) +{ + static_assert(id == F::PACKET_ID, "F::PACKET_ID"); + static_assert(size == sizeof(typename F::NetType), "F::NetType"); + + typename F::NetType net_fixed; + RecvResult rv = net_recv_fpacket(s, net_fixed); + assert (fixed.magic_packet_id == F::PACKET_ID); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&fixed, net_fixed)) + return RecvResult::Error; + } + return rv; +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_vpacket(Session *s, H& head, std::vector& repeat) +{ + static_assert(id == H::PACKET_ID, "H::PACKET_ID"); + static_assert(headsize == sizeof(typename H::NetType), "H::NetType"); + static_assert(id == R::PACKET_ID, "R::PACKET_ID"); + static_assert(repeatsize == sizeof(typename R::NetType), "R::NetType"); + + typename H::NetType net_head; + std::vector net_repeat; + RecvResult rv = net_recv_vpacket(s, net_head, net_repeat); + assert (head.magic_packet_id == H::PACKET_ID); + if (rv == RecvResult::Complete) + { + if (!network_to_native(&head, net_head)) + return RecvResult::Error; + 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; +} + +// convenience for trailing strings + +struct VarStringNetType +{ + char c; +}; + +template +void send_vpacket(Session *s, H& head, const XString& repeat) +{ + static_assert(id == H::PACKET_ID, "H::PACKET_ID"); + static_assert(headsize == sizeof(typename H::NetType), "H::NetType"); + // static_assert(id == R::PACKET_ID, "R::PACKET_ID"); + static_assert(repeatsize == 1, "R::NetType"); + + typename H::NetType net_head; + // since it's already allocated, it can't overflow address space + size_t total_length = sizeof(typename H::NetType) + (repeat.size() + 1) * sizeof(VarStringNetType); + head.magic_packet_length = total_length; + if (head.magic_packet_length != total_length) + { + s->set_eof(); + return; + } + // TODO potentially avoid the allocation + std::vector net_repeat(repeat.size() + 1); + if (!native_to_network(&net_head, head)) + { + s->set_eof(); + return; + } + for (size_t i = 0; i < repeat.size(); ++i) + { + net_repeat[i].c = repeat[i]; + } + net_repeat[repeat.size()].c = '\0'; + SendResult rv = net_send_vpacket(s, net_head, net_repeat); + if (rv != SendResult::Success) + s->set_eof(); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_vpacket(Session *s, H& head, AString& repeat) +{ + static_assert(id == H::PACKET_ID, "H::PACKET_ID"); + static_assert(headsize == sizeof(typename H::NetType), "H::NetType"); + //static_assert(id == R::PACKET_ID, "R::PACKET_ID"); + static_assert(repeatsize == 1, "R::NetType"); + + typename H::NetType net_head; + std::vector net_repeat; + RecvResult rv = net_recv_vpacket(s, net_head, net_repeat); + assert (head.magic_packet_id == H::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 + 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 + +// TODO make this go away with template specialization + +template +struct NetCommonPacketHead +{ + Little16 magic_packet_id; + Little16 magic_packet_length; +}; + +template +struct CommonPacketHead +{ + using NetType = NetCommonPacketHead; + static const uint16_t PACKET_ID = PKT_ID; + + uint16_t magic_packet_id = PACKET_ID; + uint16_t magic_packet_length; +}; + +template +bool native_to_network(NetCommonPacketHead *net, CommonPacketHead nat) +{ + return native_to_network(&net->magic_packet_id, nat.magic_packet_id) + && native_to_network(&net->magic_packet_length, nat.magic_packet_length); +} + +template +bool network_to_native(CommonPacketHead *nat, NetCommonPacketHead net) +{ + return network_to_native(&nat->magic_packet_id, net.magic_packet_id) + && network_to_native(&nat->magic_packet_length, net.magic_packet_length); +} + +template +void send_packet_repeatonly(Session *s, const std::vector& v) +{ + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == R::PACKET_ID, "R::PACKET_ID"); + static_assert(repeatsize == sizeof(typename R::NetType), "R::NetType"); + + CommonPacketHead head; + send_vpacket(s, head, v); +} + +template +__attribute__((warn_unused_result)) +RecvResult recv_packet_repeatonly(Session *s, std::vector& v) +{ + static_assert(headsize == 4, "repeat headsize"); + static_assert(id == R::PACKET_ID, "R::PACKET_ID"); + static_assert(repeatsize == sizeof(typename R::NetType), "R::NetType"); + + CommonPacketHead head; + return recv_vpacket(s, head, v); +} + +#endif // TMWA_NET_PACKETS_HPP diff --git a/src/net/socket.cpp b/src/net/socket.cpp index 6880bfa..e9e819e 100644 --- a/src/net/socket.cpp +++ b/src/net/socket.cpp @@ -1,4 +1,5 @@ #include "socket.hpp" +#include "vomit.hpp" // for remaining FIFO functions // socket.cpp - Network event system. // // Copyright © ????-2004 Athena Dev Teams diff --git a/src/net/socket.hpp b/src/net/socket.hpp index aff8278..2b60ac6 100644 --- a/src/net/socket.hpp +++ b/src/net/socket.hpp @@ -173,17 +173,4 @@ void do_sendrecv(interval_t next); /// Call the parser function for every socket that has read data void do_parsepacket(void); -/// Check how much can be read -inline -size_t RFIFOREST(Session *s) -{ - return s->rdata_size - s->rdata_pos; -} - -/// Done reading -void RFIFOSKIP(Session *s, size_t len); - -/// Finish writing -void WFIFOSET(Session *s, size_t len); - #endif // TMWA_NET_SOCKET_HPP diff --git a/src/net/vomit.hpp b/src/net/vomit.hpp index 3d3a07f..84c54ac 100644 --- a/src/net/vomit.hpp +++ b/src/net/vomit.hpp @@ -25,6 +25,23 @@ # include "socket.hpp" +// these first three are really in socket.cpp +// but here for cleanliness + +/// Check how much can be read +inline +size_t RFIFOREST(Session *s) +{ + return s->rdata_size - s->rdata_pos; +} + +/// Done reading +void RFIFOSKIP(Session *s, size_t len); + +/// Finish writing +void WFIFOSET(Session *s, size_t len); + + template uint8_t *pod_addressof_m(T& structure) { -- cgit v1.2.3-70-g09d2