aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2018-06-26 12:58:54 -0400
committerDeterminant <ted.sybil@gmail.com>2018-06-26 12:58:54 -0400
commit5c3b39340d365f5ff37a79424956591e87b44816 (patch)
tree45fc59c19ed95c44bacbbe59396d8bc1b25872dd
init
-rw-r--r--.gitignore16
-rw-r--r--CMakeLists.txt56
-rw-r--r--LICENSE21
-rw-r--r--cmake/Modules/FindLibevent.cmake49
-rw-r--r--include/salticidae/conn.h235
-rw-r--r--include/salticidae/crypto.h78
-rw-r--r--include/salticidae/msg.h253
-rw-r--r--include/salticidae/netaddr.h115
-rw-r--r--include/salticidae/network.h552
-rw-r--r--include/salticidae/ref.h202
-rw-r--r--include/salticidae/stream.h274
-rw-r--r--include/salticidae/type.h67
-rw-r--r--include/salticidae/util.h292
-rw-r--r--src/conn.cpp277
-rw-r--r--src/util.cpp244
-rw-r--r--test/.gitignore1
-rw-r--r--test/CMakeLists.txt24
-rw-r--r--test/Makefile180
-rw-r--r--test/test_msg.cpp74
19 files changed, 3010 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..475a195
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+CMakeFiles/
+cmake_install.cmake
+CMakeDoxygenDefaults.cmake
+CMakeDoxyfile.in
+CMakeCache.txt
+cmake-build-debug/
+libsalticidae.a
+src/*.swo
+src/*.swp
+*.a
+*.o
+*.la
+*.lo
+*.so
+*.gch
+/Makefile
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..5931322
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,56 @@
+# Copyright (c) 2018 Cornell University.
+#
+# Author: Ted Yin <tederminant@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is furnished to do
+# so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+cmake_minimum_required(VERSION 3.9)
+project(Salticidae)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
+
+find_package(Libevent REQUIRED)
+find_package(OpenSSL REQUIRED)
+
+include_directories(include)
+add_library(salticidae
+ src/util.cpp
+ src/conn.cpp)
+target_link_libraries(salticidae event crypto)
+
+add_subdirectory(test)
+
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Release")
+endif()
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ add_definitions(-DHOTSTUFF_DEBUG_LOG)
+elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
+ add_definitions(-DHOTSTUFF_NORMAL_LOG)
+endif()
+
+#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -no-pie -pg")
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -W -Wall -Wextra -pedantic")
+
+macro(remove_cxx_flag flag)
+ string(REPLACE "${flag}" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
+endmacro()
+
+remove_cxx_flag("-DNDEBUG")
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..11712af
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2018 Cornell University.
+
+Author: Ted Yin <tederminant@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/cmake/Modules/FindLibevent.cmake b/cmake/Modules/FindLibevent.cmake
new file mode 100644
index 0000000..e874e67
--- /dev/null
+++ b/cmake/Modules/FindLibevent.cmake
@@ -0,0 +1,49 @@
+# Copyright (c) 2018 Cornell University.
+#
+# Author: Ted Yin <tederminant@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is furnished to do
+# so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# This module will define:
+# - Libevent_FOUND - if the library is found
+# - LIBEVENT_INC - include directories
+# - LIBEVENT_LIB - the libraries required to use libevent
+
+set(LIBEVENT_PREFIXES /usr/local /opt/local)
+
+foreach(prefix ${LIBEVENT_PREFIXES})
+ list(APPEND LIBEVENT_LIB_PATH "${prefix}/lib")
+ list(APPEND LIBEVENT_INC_PATH "${prefix}/include")
+endforeach()
+
+find_library(LIBEVENT_LIB NAMES event PATHS ${LIBEVENT_LIB_PATH})
+find_path(LIBEVENT_INC event.h PATHS ${LIBEVENT_INC_PATH})
+
+if (LIBEVENT_LIB AND LIBEVENT_INC)
+ set(Libevent_FOUND TRUE)
+ if (NOT Libevent_FIND_QUIETLY)
+ message(STATUS "Found libevent: ${LIBEVENT_LIB}")
+ endif ()
+else ()
+ set(Libevent_FOUND FALSE)
+ if (Libevent_FIND_REQUIRED)
+ message(FATAL_ERROR "Could NOT find libevent.")
+ endif ()
+ message(STATUS "libevent NOT found.")
+endif ()
diff --git a/include/salticidae/conn.h b/include/salticidae/conn.h
new file mode 100644
index 0000000..40facc9
--- /dev/null
+++ b/include/salticidae/conn.h
@@ -0,0 +1,235 @@
+/**
+ * Copyright (c) 2018 Cornell University.
+ *
+ * Author: Ted Yin <tederminant@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SALTICIDAE_CONN_H
+#define _SALTICIDAE_CONN_H
+
+#include <cassert>
+#include <cstdint>
+#include <event2/event.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include <string>
+#include <unordered_map>
+#include <list>
+#include <algorithm>
+#include <exception>
+
+#include "salticidae/type.h"
+#include "salticidae/ref.h"
+#include "salticidae/util.h"
+#include "salticidae/netaddr.h"
+#include "salticidae/msg.h"
+
+const int MAX_LISTEN_BACKLOG = 10;
+const size_t BUFF_SEG_SIZE = 4096;
+const size_t MAX_MSG_HANDLER = 64;
+const double TRY_CONN_DELAY = 2;
+const double CONN_SERVER_TIMEOUT = 2;
+
+namespace salticidae {
+
+inline double gen_rand_timeout(double base_timeout) {
+ return base_timeout + rand() / (double)RAND_MAX * 0.5 * base_timeout;
+}
+
+class RingBuffer {
+ struct buffer_entry_t {
+ bytearray_t data;
+ bytearray_t::iterator offset;
+ buffer_entry_t(bytearray_t &&_data): data(std::move(_data)) {
+ offset = data.begin();
+ }
+
+ buffer_entry_t(buffer_entry_t &&other) {
+ size_t _offset = other.offset - other.data.begin();
+ data = std::move(other.data);
+ offset = data.begin() + _offset;
+ }
+
+ buffer_entry_t(const buffer_entry_t &other): data(other.data) {
+ offset = data.begin() + (other.offset - other.data.begin());
+ }
+
+ size_t length() const { return data.end() - offset; }
+ };
+ std::list<buffer_entry_t> ring;
+ size_t _size;
+
+ public:
+ RingBuffer(): _size(0) {}
+ ~RingBuffer() { clear(); }
+ RingBuffer &operator=(const RingBuffer &other) = delete;
+ RingBuffer(const RingBuffer &other) = delete;
+ RingBuffer &operator=(RingBuffer &&other) {
+ ring = std::move(other.ring);
+ _size = other._size;
+ other._size = 0;
+ return *this;
+ }
+
+ void push(bytearray_t &&data) {
+ _size += data.size();
+ ring.push_back(buffer_entry_t(std::move(data)));
+ }
+
+ bytearray_t pop(size_t len) {
+ bytearray_t res;
+ auto i = ring.begin();
+ while (len && i != ring.end())
+ {
+ size_t copy_len = std::min(i->length(), len);
+ res.insert(res.end(), i->offset, i->offset + copy_len);
+ i->offset += copy_len;
+ len -= copy_len;
+ if (i->offset == i->data.end())
+ i++;
+ }
+ ring.erase(ring.begin(), i);
+ _size -= res.size();
+ return std::move(res);
+ }
+
+ size_t size() const { return _size; }
+
+ void clear() {
+ ring.clear();
+ _size = 0;
+ }
+};
+
+class ConnPoolError: public SalticidaeError {
+ using SalticidaeError::SalticidaeError;
+};
+
+/** The connection pool. */
+class ConnPool {
+ public:
+ class Conn;
+ using conn_t = RcObj<Conn>;
+ /** The abstraction for a bi-directional connection. */
+ class Conn {
+ public:
+ enum ConnMode {
+ ACTIVE, /**< the connection is established by connect() */
+ PASSIVE, /**< the connection is established by accept() */
+ };
+
+ private:
+ conn_t self_ref;
+ int fd;
+ ConnPool *cpool;
+ ConnMode mode;
+ NetAddr addr;
+
+ RingBuffer send_buffer;
+ RingBuffer recv_buffer;
+
+ Event ev_read;
+ Event ev_write;
+ Event ev_connect;
+ /** does not need to wait if true */
+ bool ready_send;
+
+ void recv_data(evutil_socket_t, short);
+ void send_data(evutil_socket_t, short);
+ void conn_server(evutil_socket_t, short);
+ void try_conn(evutil_socket_t, short);
+
+ public:
+ friend ConnPool;
+ Conn(): self_ref(this) {}
+
+ virtual ~Conn() {
+ SALTICIDAE_LOG_INFO("destroyed connection %s", std::string(*this).c_str());
+ }
+
+ conn_t self() { return self_ref; }
+ operator std::string() const;
+ int get_fd() const { return fd; }
+ const NetAddr &get_addr() const { return addr; }
+ ConnMode get_mode() const { return mode; }
+ RingBuffer &read() { return recv_buffer; }
+
+ void write(bytearray_t &&data) {
+ send_buffer.push(std::move(data));
+ if (ready_send)
+ send_data(fd, EV_WRITE);
+ }
+
+ void move_send_buffer(conn_t other) {
+ send_buffer = std::move(other->send_buffer);
+ }
+
+ void terminate();
+
+ protected:
+ /** close the connection and free all on-going or planned events. */
+ virtual void close() {
+ ev_read.clear();
+ ev_write.clear();
+ ev_connect.clear();
+ ::close(fd);
+ fd = -1;
+ }
+
+ virtual void on_read() = 0;
+ virtual void on_setup() = 0;
+ virtual void on_teardown() = 0;
+ };
+
+ private:
+ std::unordered_map<int, conn_t> pool;
+ int listen_fd;
+ Event ev_listen;
+
+ void accept_client(evutil_socket_t, short);
+ conn_t add_conn(conn_t conn);
+
+ protected:
+ struct event_base *eb;
+ virtual conn_t create_conn() = 0;
+
+ public:
+ friend Conn;
+ ConnPool(struct event_base *eb): eb(eb) {}
+
+ ~ConnPool() {
+ for (auto it: pool)
+ {
+ conn_t conn = it.second;
+ conn->close();
+ }
+ }
+
+ /** create an active mode connection to addr */
+ conn_t create_conn(const NetAddr &addr);
+ /** setup and start listening */
+ void init(NetAddr listen_addr);
+};
+
+}
+
+#endif
diff --git a/include/salticidae/crypto.h b/include/salticidae/crypto.h
new file mode 100644
index 0000000..c329c63
--- /dev/null
+++ b/include/salticidae/crypto.h
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2018 Cornell University.
+ *
+ * Author: Ted Yin <tederminant@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SALTICIDAE_CRYPTO_H
+#define _SALTICIDAE_CRYPTO_H
+
+#include "salticidae/type.h"
+#include <openssl/sha.h>
+
+namespace salticidae {
+
+class SHA256 {
+ SHA256_CTX *ctx;
+
+ public:
+ SHA256(): ctx(new SHA256_CTX()) { reset(); }
+ ~SHA256() { delete ctx; }
+
+ void reset() {
+ if (!SHA256_Init(ctx))
+ throw std::runtime_error("openssl SHA256 init error");
+ }
+
+ template<typename T>
+ void update(const T &data) {
+ update(reinterpret_cast<const uint8_t *>(&*data.begin()), data.size());
+ }
+
+ void update(const bytearray_t::const_iterator &it, size_t length) {
+ update(&*it, length);
+ }
+
+ void update(const uint8_t *ptr, size_t length) {
+ if (!SHA256_Update(ctx, ptr, length))
+ throw std::runtime_error("openssl SHA256 update error");
+ }
+
+ void _digest(bytearray_t &md) {
+ if (!SHA256_Final(&*md.begin(), ctx))
+ throw std::runtime_error("openssl SHA256 error");
+ }
+
+ void digest(bytearray_t &md) {
+ md.resize(32);
+ _digest(md);
+ }
+
+ bytearray_t digest() {
+ bytearray_t md(32);
+ _digest(md);
+ return std::move(md);
+ }
+};
+
+}
+
+#endif
diff --git a/include/salticidae/msg.h b/include/salticidae/msg.h
new file mode 100644
index 0000000..62fc33b
--- /dev/null
+++ b/include/salticidae/msg.h
@@ -0,0 +1,253 @@
+/**
+ * Copyright (c) 2018 Cornell University.
+ *
+ * Author: Ted Yin <tederminant@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SALTICIDAE_MSG_H
+#define _SALTICIDAE_MSG_H
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include "salticidae/type.h"
+#include "salticidae/stream.h"
+#include "salticidae/netaddr.h"
+
+namespace salticidae {
+
+template<typename OpcodeType = uint8_t,
+ const OpcodeType PING = 0xf0,
+ const OpcodeType PONG = 0xf1>
+class MsgBase {
+ public:
+ using opcode_t = OpcodeType;
+ static const opcode_t OPCODE_PING = PING;
+ static const opcode_t OPCODE_PONG = PONG;
+ static const size_t header_size;
+
+ private:
+ /* header */
+ /* all integers are encoded in little endian in the protocol */
+ uint32_t magic;
+ opcode_t opcode;
+ uint32_t length;
+ uint32_t checksum;
+
+ mutable bytearray_t payload;
+ mutable bool no_payload;
+
+ public:
+ MsgBase(): magic(0x0), no_payload(true) {}
+
+ MsgBase(const MsgBase &other):
+ magic(other.magic),
+ opcode(other.opcode),
+ length(other.length),
+ checksum(other.checksum),
+ payload(other.payload),
+ no_payload(other.no_payload) {}
+
+ MsgBase(MsgBase &&other):
+ magic(other.magic),
+ opcode(std::move(other.opcode)),
+ length(other.length),
+ checksum(other.checksum),
+ payload(std::move(other.payload)),
+ no_payload(other.no_payload) {}
+
+ MsgBase(const uint8_t *raw_header) {
+ uint32_t _magic;
+ opcode_t _opcode;
+ uint32_t _length;
+ uint32_t _checksum;
+ DataStream s(raw_header, raw_header + MsgBase::header_size);
+
+ s >> _magic
+ >> _opcode
+ >> _length
+ >> _checksum;
+ magic = letoh(_magic);
+ opcode = _opcode;
+ length = letoh(_length);
+ checksum = letoh(_checksum);
+ }
+
+ MsgBase &operator=(const MsgBase &other) {
+ magic = other.magic;
+ opcode = other.opcode;
+ length = other.length;
+ checksum = other.checksum;
+ payload = other.payload;
+ no_payload = other.no_payload;
+ return *this;
+ }
+
+ MsgBase &operator=(MsgBase &&other) {
+ magic = other.magic;
+ opcode = std::move(other.opcode);
+ length = other.length;
+ checksum = other.checksum;
+ payload = std::move(other.payload);
+ no_payload = other.no_payload;
+ return *this;
+ }
+
+ ~MsgBase() {}
+
+ size_t get_length() const { return length; }
+
+ const opcode_t &get_opcode() const { return opcode; }
+
+ void set_opcode(const opcode_t &_opcode) {
+ opcode = _opcode;
+ }
+
+ bytearray_t &&get_payload() const {
+#ifndef SALTICIDAE_NOCHECK
+ if (no_payload)
+ throw std::runtime_error("payload not available");
+ no_payload = true;
+#endif
+ return std::move(payload);
+ }
+
+ void set_payload(DataStream &&s) {
+ set_payload(bytearray_t(std::move(s)));
+ }
+
+ void set_payload(bytearray_t &&_payload) {
+ payload = std::move(_payload);
+ length = payload.size();
+ checksum = get_checksum();
+#ifndef SALTICIDAE_NOCHECK
+ no_payload = false;
+#endif
+ }
+
+ operator std::string() const {
+ DataStream s;
+ s << "<"
+ << "magic=" << get_hex(magic) << " "
+ << "opcode=" << get_hex(opcode) << " "
+ << "length=" << get_hex(length) << " "
+ << "checksum=" << get_hex(checksum) << " "
+ << "payload=" << get_hex(payload) << ">";
+
+ //std::string opcode_hex = get_hex(opcode);
+ //char *buff = new char[128 + opcode_hex.size()];
+ //size_t ret = sprintf(buff,
+ // "<magic=%08x opcode=%s length=%08x checksum=%08x payload=",
+ // magic, opcode_hex.c_str(), length, checksum);
+ //buff[ret] = 0;
+ //std::string res = std::string(buff) + bin2hexstr(payload.data(), length) + ">";
+ //delete [] buff;
+ //return std::move(res);
+ return std::string(s);
+ }
+
+ uint32_t get_checksum() const {
+ static class SHA256 sha256;
+ uint32_t res;
+ bytearray_t tmp;
+ sha256.reset();
+ sha256.update(payload);
+ sha256.digest(tmp);
+ sha256.reset();
+ sha256.update(tmp);
+ sha256.digest(tmp);
+ memmove(&res, &*tmp.begin(), 4);
+ return res;
+ }
+
+ bool verify_checksum() const {
+ return checksum == get_checksum();
+ }
+
+ bytearray_t serialize() const {
+ DataStream s;
+ s << htole(magic)
+ << opcode
+ << htole(length)
+ << htole(checksum)
+ << payload;
+ return std::move(s);
+ }
+
+ void gen_ping(uint16_t port) {
+ DataStream s;
+ set_opcode(OPCODE_PING);
+ s << htole(port);
+ set_payload(std::move(s));
+ }
+
+ void parse_ping(uint16_t &port) const {
+ DataStream s(get_payload());
+ s >> port;
+ port = letoh(port);
+ }
+
+ void gen_pong(uint16_t port) {
+ DataStream s;
+ set_opcode(OPCODE_PONG);
+ s << htole(port);
+ set_payload(std::move(s));
+ }
+
+ void parse_pong(uint16_t &port) const {
+ DataStream s(get_payload());
+ s >> port;
+ port = letoh(port);
+ }
+
+ void gen_hash_list(DataStream &s,
+ const std::vector<uint256_t> &hashes) {
+ uint32_t size = htole((uint32_t)hashes.size());
+ s << size;
+ for (const auto &h: hashes) s << h;
+ }
+
+ void parse_hash_list(DataStream &s,
+ std::vector<uint256_t> &hashes) const {
+ uint32_t size;
+ hashes.clear();
+
+ s >> size;
+ size = letoh(size);
+
+ hashes.resize(size);
+ for (auto &hash: hashes) s >> hash;
+ }
+
+};
+
+template<typename OpcodeType,
+ OpcodeType _,
+ OpcodeType __>
+const size_t MsgBase<OpcodeType, _, __>::header_size =
+ sizeof(MsgBase<OpcodeType, _, __>) -
+ sizeof(MsgBase<OpcodeType, _, __>::payload) -
+ sizeof(MsgBase<OpcodeType, _, __>::no_payload);
+}
+
+#endif
diff --git a/include/salticidae/netaddr.h b/include/salticidae/netaddr.h
new file mode 100644
index 0000000..c166c3a
--- /dev/null
+++ b/include/salticidae/netaddr.h
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2018 Cornell University.
+ *
+ * Author: Ted Yin <tederminant@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SALTICIDAE_NETADDR_H
+#define _SALTICIDAE_NETADDR_H
+
+#include <string>
+#include <cstring>
+#include <cstdint>
+#include <arpa/inet.h>
+
+#include "salticidae/util.h"
+
+namespace salticidae {
+
+/* TODO: IPv6 support */
+
+struct NetAddr {
+ uint32_t ip;
+ uint16_t port;
+ /* construct from human-readable format */
+ NetAddr(): ip(0), port(0) {}
+
+ NetAddr(uint32_t ip, uint16_t port): ip(ip), port(port) {}
+
+ NetAddr(const std::string &_addr, uint16_t _port) {
+ set_by_ip_port(_addr, _port);
+ }
+
+ void set_by_ip_port(const std::string &_addr, uint16_t _port) {
+ struct hostent *h;
+ if ((h = gethostbyname(_addr.c_str())) == nullptr)
+ throw SalticidaeError("gethostbyname failed");
+ memmove(&ip, h->h_addr_list[0], sizeof(in_addr_t));
+ port = htons(_port);
+ }
+
+ NetAddr(const std::string &ip_port_addr) {
+ size_t pos = ip_port_addr.find(":");
+ if (pos == std::string::npos)
+ throw SalticidaeError("invalid port format");
+ std::string ip_str = ip_port_addr.substr(0, pos);
+ std::string port_str = ip_port_addr.substr(pos + 1);
+ long port;
+ try {
+ port = std::stol(port_str.c_str());
+ } catch (std::logic_error) {
+ throw SalticidaeError("invalid port format");
+ }
+ if (port < 0)
+ throw SalticidaeError("negative port number");
+ if (port > 0xffff)
+ throw SalticidaeError("port number greater than 0xffff");
+ set_by_ip_port(ip_str, (uint16_t)port);
+ }
+ /* construct from unix socket format */
+ NetAddr(const struct sockaddr_in *addr_sock) {
+ ip = addr_sock->sin_addr.s_addr;
+ port = addr_sock->sin_port;
+ }
+
+ bool operator==(const NetAddr &other) const {
+ return ip == other.ip && port == other.port;
+ }
+
+ operator std::string() const {
+ struct in_addr in;
+ in.s_addr = ip;
+ return "<NetAddr " + std::string(inet_ntoa(in)) +
+ ":" + std::to_string(ntohs(port)) + ">";
+ }
+
+ bool is_null() const { return ip == 0 && port == 0; }
+};
+
+}
+
+namespace std {
+ template <>
+ struct hash<salticidae::NetAddr> {
+ size_t operator()(const salticidae::NetAddr &k) const {
+ return k.ip ^ k.port;
+ }
+ };
+
+ template <>
+ struct hash<const salticidae::NetAddr> {
+ size_t operator()(const salticidae::NetAddr &k) const {
+ return k.ip ^ k.port;
+ }
+ };
+}
+
+#endif
diff --git a/include/salticidae/network.h b/include/salticidae/network.h
new file mode 100644
index 0000000..3b82927
--- /dev/null
+++ b/include/salticidae/network.h
@@ -0,0 +1,552 @@
+/**
+ * Copyright (c) 2018 Cornell University.
+ *
+ * Author: Ted Yin <tederminant@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SALTICIDAE_NETWORK_H
+#define _SALTICIDAE_NETWORK_H
+
+#include "salticidae/netaddr.h"
+#include "salticidae/msg.h"
+#include "salticidae/conn.h"
+
+namespace salticidae {
+
+/** Network of nodes who can send async messages. */
+template<typename MsgType>
+class MsgNetwork: public ConnPool {
+ public:
+ class Conn: public ConnPool::Conn {
+ enum MsgState {
+ HEADER,
+ PAYLOAD
+ };
+ MsgType msg;
+ MsgState msg_state;
+ MsgNetwork *mn;
+
+ protected:
+ mutable size