summaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-05-18 02:43:55 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-05-19 17:10:09 -0700
commit2410aeb608329ed57a31315effdfd5a751788616 (patch)
tree4b961100e9aec99c16c6ccae7f1857e763d58b14 /src/net
parentfccd01bd0fba297531595b51ffb8db044ed6f22c (diff)
downloadtmwa-2410aeb608329ed57a31315effdfd5a751788616.tar.gz
tmwa-2410aeb608329ed57a31315effdfd5a751788616.tar.bz2
tmwa-2410aeb608329ed57a31315effdfd5a751788616.tar.xz
tmwa-2410aeb608329ed57a31315effdfd5a751788616.zip
Convert login/char and login/admin server components to proto-v2
Diffstat (limited to 'src/net')
-rw-r--r--src/net/packets.cpp89
-rw-r--r--src/net/packets.hpp354
-rw-r--r--src/net/socket.cpp1
-rw-r--r--src/net/socket.hpp13
-rw-r--r--src/net/vomit.hpp17
5 files changed, 461 insertions, 13 deletions
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 <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 "../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<const Byte *>(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<Byte *>(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 <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 "../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<Byte *>(&id), 2);
+ if (okay)
+ {
+ if (!network_to_native(packet_id, id))
+ {
+ s->set_eof();
+ return false;
+ }
+ }
+ return okay;
+}
+
+template<class F>
+__attribute__((warn_unused_result))
+SendResult net_send_fpacket(Session *s, const F& fixed)
+{
+ bool ok = packet_send(s, reinterpret_cast<const Byte *>(&fixed), sizeof(F));
+ return ok ? SendResult::Success : SendResult::Fail;
+}
+
+template<class H, class R>
+__attribute__((warn_unused_result))
+SendResult net_send_vpacket(Session *s, const H& head, const std::vector<R>& repeat)
+{
+ bool ok = packet_send(s, reinterpret_cast<const Byte *>(&head), sizeof(H));
+ ok &= packet_send(s, reinterpret_cast<const Byte *>(repeat.data()), repeat.size() * sizeof(R));
+ return ok ? SendResult::Success : SendResult::Fail;
+}
+
+template<class F>
+__attribute__((warn_unused_result))
+RecvResult net_recv_fpacket(Session *s, F& fixed)
+{
+ bool ok = packet_fetch(s, 0, reinterpret_cast<Byte *>(&fixed), sizeof(F));
+ if (ok)
+ {
+ packet_discard(s, sizeof(F));
+ return RecvResult::Complete;
+ }
+ return RecvResult::Incomplete;
+}
+
+template<class HNat, class H, class R>
+__attribute__((warn_unused_result))
+RecvResult net_recv_vpacket(Session *s, H& head, std::vector<R>& repeat)
+{
+ bool ok = packet_fetch(s, 0, reinterpret_cast<Byte *>(&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<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, uint16_t size, class F>
+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<uint16_t id, uint16_t headsize, uint16_t repeatsize, class H, class R>
+void send_vpacket(Session *s, H& head, const std::vector<R>& 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<typename R::NetType> 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<uint16_t id, uint16_t size, class F>
+__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<uint16_t id, uint16_t headsize, uint16_t repeatsize, class H, class R>
+__attribute__((warn_unused_result))
+RecvResult recv_vpacket(Session *s, H& head, std::vector<R>& 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<typename R::NetType> net_repeat;
+ RecvResult rv = net_recv_vpacket<H>(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<uint16_t id, uint16_t headsize, uint16_t repeatsize, class H>
+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<VarStringNetType> 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<uint16_t id, uint16_t headsize, uint16_t repeatsize, class H>
+__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<VarStringNetType> net_repeat;
+ RecvResult rv = net_recv_vpacket<H>(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<const char *>(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<uint16_t PKT_ID>
+struct NetCommonPacketHead
+{
+ Little16 magic_packet_id;
+ Little16 magic_packet_length;
+};
+
+template<uint16_t PKT_ID>
+struct CommonPacketHead
+{
+ using NetType = NetCommonPacketHead<PKT_ID>;
+ static const uint16_t PACKET_ID = PKT_ID;
+
+ uint16_t magic_packet_id = PACKET_ID;
+ uint16_t magic_packet_length;
+};
+
+template<uint16_t PKT_ID>
+bool native_to_network(NetCommonPacketHead<PKT_ID> *net, CommonPacketHead<PKT_ID> 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<uint16_t PKT_ID>
+bool network_to_native(CommonPacketHead<PKT_ID> *nat, NetCommonPacketHead<PKT_ID> 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<uint16_t id, uint16_t headsize, uint16_t repeatsize, class R>
+void send_packet_repeatonly(Session *s, const std::vector<R>& 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<R::PACKET_ID> head;
+ send_vpacket<id, 4, repeatsize>(s, head, v);
+}
+
+template<uint16_t id, uint16_t headsize, uint16_t repeatsize, class R>
+__attribute__((warn_unused_result))
+RecvResult recv_packet_repeatonly(Session *s, std::vector<R>& 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<R::PACKET_ID> head;
+ return recv_vpacket<id, 4, repeatsize>(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<class T>
uint8_t *pod_addressof_m(T& structure)
{