This commit is contained in:
Jeff Becker 2018-09-06 07:46:19 -04:00
parent ba648868c4
commit 60d5277351
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05
25 changed files with 548 additions and 476 deletions

View File

@ -19,7 +19,7 @@ else()
CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17)
endif()
option(HAVE_CXX17_FILESYSTEM "Enable if your C++ compiler and runtime library implements std::[experimental::]filesystem" OFF)
option(HAVE_CXX17_FILESYSTEM "Disable if your C++ compiler and runtime library lacks std::[experimental::]filesystem" ON)
if(COMPILER_SUPPORTS_CXX11 AND NOT HAVE_CXX17_FILESYSTEM)
add_cxxflags("-std=c++11")
@ -47,7 +47,7 @@ else()
set(THREAD_LIB pthread)
endif()
add_cxxflags("-fno-rtti -fno-exceptions -fpermissive")
add_cxxflags("-fno-exceptions -fpermissive -fno-rtti")
if (NOT MSVC)
add_cflags("-march=native")
@ -146,7 +146,14 @@ else()
)
endif()
set(LIBS ${SODIUM_LIB} ${THREAD_LIB} jemalloc)
if(JEMALLOC)
set(MALLOC_LIB jemalloc)
endif()
set(LIBS ${SODIUM_LIB} ${THREAD_LIB} ${MALLOC_LIB})
if(HAVE_CXX17_FILESYSTEM)
set(LIBS ${LIBS} stdc++fs)
endif()
set(LIB lokinet)
set(SHARED_LIB ${LIB})
@ -368,7 +375,6 @@ set(LIB_SRC
llarp/dht/publish_intro.cpp
llarp/handlers/tun.cpp
llarp/link/curvecp.cpp
llarp/link/encoder.cpp
llarp/link/server.cpp
llarp/link/utp.cpp
llarp/routing/dht_message.cpp

View File

@ -43,7 +43,7 @@ clean:
rm -f *.a *.so
debug-configure:
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DTUNTAP=ON
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DTUNTAP=ON -DJEMALLOC=ON
release-configure: clean
cmake -GNinja -DSTATIC_LINK=ON -DCMAKE_BUILD_TYPE=Release -DRELEASE_MOTTO="$(shell cat motto.txt)" -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DTUNTAP=ON

View File

@ -25,6 +25,21 @@ namespace llarp
struct in6_addr ip;
uint16_t port;
AddressInfo() : IBEncodeMessage()
{
}
AddressInfo(const AddressInfo& other)
: IBEncodeMessage()
, rank(other.rank)
, dialect(other.dialect)
, pubkey(other.pubkey)
{
port = other.port;
version = other.version;
memcpy(ip.s6_addr, other.ip.s6_addr, 16);
}
~AddressInfo();
AddressInfo&

View File

@ -240,15 +240,23 @@ namespace llarp
}
// TODO: check for shadowed values elsewhere
uint64_t version = 0;
uint64_t version = LLARP_PROTO_VERSION;
static bool
OnKey(dict_reader* r, llarp_buffer_t* k)
{
if(k)
return static_cast< IBEncodeMessage* >(r->user)->DecodeKey(*k,
r->buffer);
return true;
return static_cast< IBEncodeMessage* >(r->user)->HandleKey(k, r->buffer);
}
bool
HandleKey(llarp_buffer_t* k, llarp_buffer_t* val)
{
if(k == nullptr)
return true;
if(DecodeKey(*k, val))
return true;
llarp::LogError("unhandled key '", *k->cur, "'");
return false;
}
template < size_t bufsz, size_t align = 128 >

View File

@ -61,6 +61,8 @@ namespace llarp
parent = p;
}
virtual ~TX(){};
K target;
Context* parent;
std::set< Key_t > peersAsked;

View File

@ -58,7 +58,7 @@ namespace llarp
void
Randomize()
{
if(_data && _sz)
if(_sz)
randombytes(_data, _sz);
}

View File

@ -21,6 +21,18 @@ namespace llarp
struct in6_addr netmask;
PubKey pubkey;
ExitInfo() : IBEncodeMessage()
{
}
ExitInfo(const ExitInfo &other) : IBEncodeMessage()
{
pubkey = other.pubkey;
memcpy(address.s6_addr, other.address.s6_addr, 16);
memcpy(netmask.s6_addr, other.netmask.s6_addr, 16);
version = other.version;
}
~ExitInfo();
bool

View File

@ -15,7 +15,6 @@ namespace llarp
{
struct ILinkLayer
{
ILinkLayer(llarp_router* r);
virtual ~ILinkLayer();
bool
@ -41,10 +40,7 @@ namespace llarp
Configure(llarp_ev_loop* loop, const std::string& ifname, int af,
uint16_t port);
virtual ILinkSession*
NewInboundSession(const Addr& from) = 0;
virtual ILinkSession*
virtual std::unique_ptr< ILinkSession >
NewOutboundSession(const RouterContact& rc, const AddressInfo& ai) = 0;
virtual void
@ -129,7 +125,6 @@ namespace llarp
std::make_pair(addr, std::unique_ptr< ILinkSession >(s)));
}
llarp_router* m_router;
llarp_logic* m_Logic = nullptr;
Addr m_ourAddr;
llarp_udp_io m_udp;

View File

@ -3,6 +3,8 @@
#include <llarp/crypto.hpp>
#include <llarp/net.hpp>
#include <llarp/router_contact.hpp>
#include <functional>
namespace llarp
{
@ -14,51 +16,34 @@ namespace llarp
virtual ~ILinkSession(){};
/// called every event loop tick
virtual void
Pump(){};
std::function< void(void) > Pump;
/// called every timer tick
virtual void
Tick(llarp_time_t now){};
std::function< void(llarp_time_t) > Tick;
/// send a message buffer to the remote endpoint
virtual bool
SendMessageBuffer(llarp_buffer_t buf) = 0;
/// handle low level recv of data
virtual bool
Recv(const void* buf, size_t sz) = 0;
std::function< bool(llarp_buffer_t) > SendMessageBuffer;
/// start the connection
virtual void
Start() = 0;
std::function< void(void) > Start;
/// send a keepalive to the remote endpoint
virtual bool
SendKeepAlive() = 0;
std::function< bool(void) > SendKeepAlive;
/// send close message
virtual void
SendClose() = 0;
std::function< void(void) > SendClose;
/// return true if we are established
virtual bool
IsEstablished() const = 0;
std::function< bool(void) > IsEstablished;
/// return true if this session has timed out
virtual bool
TimedOut(llarp_time_t now) const
{
return false;
};
std::function< bool(llarp_time_t) > TimedOut;
/// get remote public identity key
virtual const PubKey&
GetPubKey() const = 0;
std::function< const PubKey&(void) > GetPubKey;
/// get remote address endpoint
virtual const Addr&
GetRemoteEndpoint() const = 0;
/// handle LIM
std::function< bool(const LinkIntroMessage*) > HandleLinkIntroMessage;
};
} // namespace llarp

View File

@ -55,6 +55,10 @@ namespace llarp
bool
MessageDone();
/// resets internal state
void
Reset();
private:
RouterID
GetCurrentFrom();
@ -63,7 +67,7 @@ namespace llarp
bool firstkey;
llarp_router* router;
ILinkSession* from = nullptr;
ILinkMessage* msg = nullptr;
std::unique_ptr< ILinkMessage > msg;
};
} // namespace llarp

View File

@ -14,14 +14,32 @@ namespace llarp
{
struct RouterContact : public IBEncodeMessage
{
RouterContact() : IBEncodeMessage()
{
Clear();
}
RouterContact(const RouterContact &other)
: addrs(other.addrs)
, enckey(other.enckey)
, pubkey(other.pubkey)
, exits(other.exits)
, signature(other.signature)
, nickname(other.nickname)
, last_updated(other.last_updated)
{
version = other.version;
}
// advertised addresses
std::vector< AddressInfo > addrs;
std::vector< AddressInfo > addrs = {};
// public encryption public key
llarp::PubKey enckey;
// public signing public key
llarp::PubKey pubkey;
// advertised exits
std::vector< ExitInfo > exits;
std::vector< ExitInfo > exits = {};
// signature
llarp::Signature signature;
/// node nickname, yw kee

View File

@ -1,4 +1,5 @@
#include <llarp/bencode.h>
#include <llarp/logger.hpp>
bool
bencode_write_bytestring(llarp_buffer_t* buff, const void* data, size_t sz)
@ -113,10 +114,14 @@ bencode_read_dict(llarp_buffer_t* buff, struct dict_reader* r)
return false;
}
if(*r->buffer->cur != 'e') // make sure we're at dictionary end
if(*r->buffer->cur != 'e')
{
llarp::LogWarn("reading dict not ending on 'e'");
// make sure we're at dictionary end
return false;
}
r->buffer->cur++;
return r->on_key(r, 0);
return r->on_key(r, nullptr);
}
bool

View File

@ -15,7 +15,7 @@
#if defined(__MINGW32__) || defined(_MSC_VER) || defined(__sun)
namespace fs = std::experimental::filesystem;
#else
namespace fs = std::filesystem;
namespace fs = std::experimental::filesystem;
#endif // end win32
#else
// not CPP17 needs this

View File

@ -1,34 +0,0 @@
#include "encoder.hpp"
namespace llarp
{
/// encode Link Introduce Message onto a buffer
/// if router is nullptr then the LIM's r member is omitted.
bool
EncodeLIM(llarp_buffer_t* buff, const llarp::RouterContact* router)
{
if(!bencode_start_dict(buff))
return false;
// message type
if(!bencode_write_bytestring(buff, "a", 1))
return false;
if(!bencode_write_bytestring(buff, "i", 1))
return false;
// router contact
if(router)
{
if(!bencode_write_bytestring(buff, "r", 1))
return false;
if(!router->BEncode(buff))
return false;
}
// version
if(!bencode_write_version_entry(buff))
return false;
return bencode_end(buff);
}
} // namespace llarp

View File

@ -3,10 +3,6 @@
namespace llarp
{
ILinkLayer::ILinkLayer(llarp_router* r) : m_router(r)
{
}
ILinkLayer::~ILinkLayer()
{
}
@ -88,9 +84,7 @@ namespace llarp
auto itr = m_Sessions.find(addr);
if(itr == m_Sessions.end())
m_Sessions
.insert(std::make_pair(
addr,
std::unique_ptr< ILinkSession >(NewOutboundSession(rc, to))))
.insert(std::make_pair(addr, std::move(NewOutboundSession(rc, to))))
.first->second->Start();
}

View File

@ -4,6 +4,7 @@
#include <llarp/buffer.hpp>
#include <llarp/endian.h>
#include <utp.h>
#include <cassert>
namespace llarp
{
@ -17,34 +18,35 @@ namespace llarp
constexpr size_t FragmentBodySize =
FragmentBufferSize - FragmentOverheadSize;
#ifdef __int128
typedef unsigned __int128 Long_t;
#else
typedef uint64_t Long_t;
#endif
typedef llarp::AlignedBuffer< FragmentBufferSize, false, Long_t >
FragmentBuffer;
typedef llarp::AlignedBuffer< MAX_LINK_MSG_SIZE, false, Long_t >
MessageBuffer;
typedef llarp::AlignedBuffer< FragmentBufferSize > FragmentBuffer;
struct LinkLayer;
struct BaseSession : public ILinkSession
{
llarp_router* router = nullptr;
RouterContact remoteRC;
utp_socket* sock;
LinkLayer* parent;
bool gotLIM;
PubKey remoteTransportPubKey;
Addr remoteAddr;
SharedSecret sessionKey;
llarp_time_t lastActive;
llarp_time_t sessionTimeout = 10 * 1000;
const static llarp_time_t sessionTimeout = 10 * 1000;
std::queue< FragmentBuffer > sendq;
FragmentBuffer recvBuf;
size_t recvBufOffset = 0;
MessageBuffer recvMsg;
size_t recvMsgOffset = 0;
size_t recvBufOffset;
std::vector< byte_t > recvMsg;
std::function< void(utp_socket*) > handleLinkEstablish;
/// base
BaseSession(llarp_router* r);
/// outbound
BaseSession(llarp_router* r, utp_socket* s, const RouterContact& rc,
const AddressInfo& addr);
/// inbound
BaseSession(llarp_router* r, utp_socket* s, const Addr& remote);
enum State
{
@ -56,26 +58,33 @@ namespace llarp
eClose // utp connection is closed
};
llarp_router*
Router();
State state;
void
EnterState(State st)
OnLinkEstablished(LinkLayer* p)
{
state = st;
lastActive = llarp_time_now_ms();
parent = p;
EnterState(eLinkEstablished);
llarp::LogDebug("link established with ", remoteAddr);
}
void
EnterState(State st);
BaseSession();
virtual ~BaseSession();
void
PumpWrite(utp_socket* sock)
PumpWrite(utp_socket* s)
{
// TODO: use utp_writev
while(sendq.size())
{
auto& front = sendq.front();
write_ll(sock, front.data(), front.size());
write_ll(s, front.data(), front.size());
sendq.pop();
}
}
@ -93,79 +102,17 @@ namespace llarp
}
bool
VerifyThenDecrypt(FragmentBuffer& buf)
{
ShortHash digest;
if(!router->crypto.hmac(
digest,
InitBuffer(buf.data() + FragmentHashSize,
FragmentBufferSize - FragmentHashSize),
sessionKey))
{
llarp::LogError("keyed hash failed");
return false;
}
if(digest != ShortHash(buf.data()))
{
llarp::LogError("Message Integrity Failed");
return false;
}
AlignedBuffer< FragmentNonceSize > nonce(buf.data() + FragmentHashSize);
auto body = InitBuffer(buf.data() + FragmentOverheadSize,
FragmentBufferSize - FragmentOverheadSize);
router->crypto.xchacha20(body, sessionKey, nonce);
uint32_t upper, lower;
if(!(llarp_buffer_read_uint32(&body, &upper)
&& llarp_buffer_read_uint32(&body, &lower)))
return false;
bool fragmentEnd = upper == 0;
if(lower > recvMsgOffset + recvMsg.size())
{
llarp::LogError("Fragment too big: ", lower, " bytes");
return false;
}
byte_t* ptr = recvMsg.data() + recvMsgOffset;
memcpy(ptr, body.cur, lower);
recvMsgOffset += lower;
if(fragmentEnd)
{
// got a message
auto msgbuf = InitBuffer(recvMsg.data(), recvMsgOffset);
recvMsgOffset = 0;
return router->HandleRecvLinkMessageBuffer(this, msgbuf);
}
return true;
}
VerifyThenDecrypt(FragmentBuffer& buf);
void
EncryptThenHash(FragmentBuffer& buf, const byte_t* ptr, uint32_t sz,
bool isLastFragment)
{
buf.Randomize();
const byte_t* nonce = buf.data() + FragmentHashSize;
byte_t* body = buf.data() + FragmentOverheadSize;
byte_t* base = body;
if(isLastFragment)
htobe32buf(body, 0);
body += sizeof(uint32_t);
htobe32buf(body, sz);
body += sizeof(uint32_t);
memcpy(body, ptr, sz);
auto payload = InitBuffer(base, FragmentBodySize);
router->crypto.xchacha20(payload, sessionKey, nonce);
router->crypto.hmac(buf, payload, sessionKey);
}
bool isLastFragment);
bool
SendMessageBuffer(llarp_buffer_t buf)
QueueWriteBuffers(llarp_buffer_t buf)
{
if(state != eSessionReady)
{
llarp::LogError("state is ", state);
return false;
}
size_t sz = buf.sz;
@ -181,23 +128,77 @@ namespace llarp
return true;
}
void
Connect()
{
utp_connect(sock, remoteAddr, remoteAddr.SockLen());
EnterState(eConnecting);
}
void
OutboundLinkEstablished(LinkLayer* p)
{
OnLinkEstablished(p);
KeyExchangeNonce nonce;
nonce.Randomize();
SendHandshake(nonce, sock);
gotLIM = true;
EnterState(eCryptoHandshake);
auto router = Router();
if(DoKeyExchange(sock, router->crypto.dh_client, nonce,
remoteTransportPubKey, router->encryption))
{
EnterState(eSessionReady);
}
}
// send our RC to the remote
void
SendHandshake(const KeyExchangeNonce& n, utp_socket* s)
{
auto buf = InitBuffer(recvBuf.data(), recvBuf.size());
// fastforward buffer for handshake to fit before
buf.cur += sizeof(uint32_t) * 2;
byte_t* begin = buf.cur;
LinkIntroMessage msg;
msg.rc = Router()->rc;
msg.N = n;
if(!msg.BEncode(&buf))
{
llarp::LogError("failed to encode our RC for handshake");
Close(s);
return;
}
uint32_t sz = buf.cur - begin;
llarp::LogDebug("handshake is of size ", sz, " bytes");
// write handshake header
buf.cur = buf.base;
llarp_buffer_put_uint32(&buf, LLARP_PROTO_VERSION);
llarp_buffer_put_uint32(&buf, sz);
// send it
write_ll(s, recvBuf.data(), sz + (sizeof(uint32_t) * 2));
sock = s;
}
bool
DoKeyExchange(utp_socket* sock, llarp_transport_dh_func dh,
DoKeyExchange(utp_socket* s, llarp_transport_dh_func dh,
const KeyExchangeNonce& n, const PubKey& other,
const SecretKey& secret)
{
sock = s;
if(!dh(sessionKey, other, secret, n))
{
llarp::LogError("key exchange with ", other, " failed");
Close(sock);
return false;
}
EnterState(eSessionReady);
return true;
}
void
Tick(llarp_time_t now)
TickImpl(llarp_time_t now)
{
}
@ -207,36 +208,26 @@ namespace llarp
return true;
}
void
LinkEstablished(utp_socket* s)
{
if(handleLinkEstablish)
handleLinkEstablish(s);
}
void
SendClose()
{
}
void
Close(utp_socket* sock)
Close(utp_socket* s)
{
if(state != eClose)
{
utp_shutdown(sock, SHUT_RDWR);
utp_close(sock);
utp_set_userdata(sock, nullptr);
utp_shutdown(s, SHUT_RDWR);
utp_close(s);
utp_set_userdata(s, nullptr);
}
EnterState(eClose);
sock = nullptr;
}
bool
IsEstablished() const override
{
return state == eSessionReady;
}
void
RecvHandshake(const void* buf, size_t bufsz, LinkLayer* p, utp_socket* s);
bool
Recv(const void* buf, size_t sz)
@ -262,74 +253,8 @@ namespace llarp
return true;
}
void
RecvHandshake(const void* buf, size_t sz, ILinkLayer* parent,
utp_socket* sock)
{
if(parent->HasSessionVia(remoteAddr))
{
Close(sock);
return;
}
llarp::LogDebug("recv handshake ", sz, " from ", remoteAddr);
if((recvBuf.size() - recvBufOffset) < sz)
{
llarp::LogDebug("handshake too big");
Close(sock);
return;
}
if(sz <= 8)
{
llarp::LogDebug("handshake too small");
Close(sock);
return;
}
memcpy(recvBuf.data(), buf, sz);
// process handshake header
uint8_t* ptr = recvBuf.data();
uint32_t version = bufbe32toh(ptr);
if(version != LLARP_PROTO_VERSION)
{
llarp::LogWarn("protocol version missmatch ", version,
" != ", LLARP_PROTO_VERSION);
return;
}
ptr += sizeof(uint32_t);
uint32_t limsz = bufbe32toh(ptr);
ptr += sizeof(uint32_t);
if(((sizeof(uint32_t) * 2) + limsz) > sz)
{
// not enough data
// TODO: don't bail here, continue reading
Close(sock);
return;
}
LinkIntroMessage msg(this);
auto mbuf = InitBuffer(ptr, limsz);
if(!msg.BDecode(&mbuf))
{
llarp::LogError("malfromed LIM from ", remoteAddr);
return;
}
if(!msg.HandleMessage(router))
{
llarp::LogError("failed to handle LIM from ", remoteAddr);
Close(sock);
return;
}
remoteRC = msg.rc;
if(!DoKeyExchange(sock, router->crypto.dh_server, msg.N, msg.rc.enckey,
parent->TransportSecretKey()))
return;
llarp::LogInfo("we got a new session from ", remoteRC.pubkey);
parent->MapAddr(remoteAddr, remoteRC.pubkey);
router->HandleLinkSessionEstablished(remoteRC.pubkey);
}
bool
TimedOut(llarp_time_t now) const
IsTimedOut(llarp_time_t now) const
{
if(now < lastActive)
return false;
@ -355,30 +280,9 @@ namespace llarp
struct LinkLayer : public ILinkLayer
{
utp_context* _utp_ctx = nullptr;
llarp_router* router = nullptr;
static uint64
OnRead(utp_callback_arguments* arg)
{
BaseSession* self =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
if(self)
{
if(self->state == BaseSession::eSessionReady)
self->Recv(arg->buf, arg->len);
else
{
LinkLayer* parent = static_cast< LinkLayer* >(
utp_context_get_userdata(arg->context));
self->RecvHandshake(arg->buf, arg->len, parent, arg->socket);
}
utp_read_drained(arg->socket);
}
else
{
llarp::LogWarn("utp_socket got data with no underlying session");
}
return 0;
}
OnRead(utp_callback_arguments* arg);
static uint64
SendTo(utp_callback_arguments* arg)
@ -390,25 +294,20 @@ namespace llarp
}
static uint64
OnConnect(utp_callback_arguments* arg)
{
BaseSession* session =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
session->LinkEstablished(arg->socket);
return 0;
}
OnStateChange(utp_callback_arguments*);
static uint64
OnAccept(utp_callback_arguments*);
LinkLayer(llarp_router* r) : ILinkLayer(r)
LinkLayer(llarp_router* r) : ILinkLayer()
{
m_router = r;
router = r;
_utp_ctx = utp_init(2);
utp_context_set_userdata(_utp_ctx, this);
utp_set_callback(_utp_ctx, UTP_SENDTO, &LinkLayer::SendTo);
utp_set_callback(_utp_ctx, UTP_ON_ACCEPT, &LinkLayer::OnAccept);
utp_set_callback(_utp_ctx, UTP_ON_CONNECT, &LinkLayer::OnConnect);
utp_set_callback(_utp_ctx, UTP_ON_STATE_CHANGE,
&LinkLayer::OnStateChange);
utp_set_callback(_utp_ctx, UTP_ON_READ, &LinkLayer::OnRead);
utp_context_set_option(_utp_ctx, UTP_LOG_NORMAL, 1);
utp_context_set_option(_utp_ctx, UTP_LOG_MTU, 1);
@ -444,19 +343,19 @@ namespace llarp
{
}
llarp_router*
GetRouter();
bool
KeyGen(SecretKey& k)
{
m_router->crypto.encryption_keygen(k);
router->crypto.encryption_keygen(k);
return true;
}
ILinkSession*
std::unique_ptr< ILinkSession >
NewOutboundSession(const RouterContact& rc, const AddressInfo& addr);
ILinkSession*
NewInboundSession(const Addr& addr);
utp_socket*
NewSocket()
{
@ -476,125 +375,108 @@ namespace llarp
return std::unique_ptr< LinkLayer >(new LinkLayer(r));
}
struct OutboundSession : public BaseSession
{
utp_socket* sock;
PubKey remoteTransportPubKey;
OutboundSession(llarp_router* r, utp_socket* s, const RouterContact& rc,
const AddressInfo& addr)
: BaseSession()
{
sock = s;
router = r;
utp_set_userdata(s, this);
remoteRC = rc;
remoteAddr = addr;
remoteTransportPubKey = addr.pubkey;
handleLinkEstablish = [=](utp_socket* usock) {
OutboundEstablish(r, usock);
};
}
void
OutboundEstablish(llarp_router* r, utp_socket* usock)
{
sock = usock;
llarp::LogDebug("link established with ", remoteAddr);
EnterState(eLinkEstablished);
KeyExchangeNonce nonce;
nonce.Randomize();
SendHandshake(nonce, r, usock);
EnterState(eCryptoHandshake);
if(DoKeyExchange(sock, r->crypto.dh_client, nonce,
remoteTransportPubKey, r->encryption))
{
LinkLayer* parent = static_cast< LinkLayer* >(
utp_context_get_userdata(utp_get_context(usock)));
parent->MapAddr(remoteAddr, remoteRC.pubkey);
r->HandleLinkSessionEstablished(remoteRC.pubkey);
}
}
// send our RC to the remote
void
SendHandshake(const KeyExchangeNonce& n, llarp_router* r, utp_socket* s)
{
auto buf = InitBuffer(recvBuf.data(), recvBuf.size());
// fastforward buffer for handshake to fit before
buf.cur += sizeof(uint32_t) * 2;
LinkIntroMessage msg;
msg.rc = r->rc;
msg.N = n;
if(!msg.BEncode(&buf))
return;
uint32_t sz = buf.cur - buf.base;
sz -= sizeof(uint32_t) * 2;
// write handshake header
buf.cur = buf.base;
llarp_buffer_put_uint32(&buf, LLARP_PROTO_VERSION);
llarp_buffer_put_uint32(&buf, sz);
// send it
write_ll(s, recvBuf.data(), sz);
sock = s;
}
void
Start()
{
utp_connect(sock, remoteAddr, remoteAddr.SockLen());
EnterState(eConnecting);
}
};
struct InboundSession : public BaseSession
{
utp_socket* sock;
InboundSession(llarp_router* r, utp_socket* s, const Addr& addr)
: BaseSession()
{
sock = s;
router = r;
utp_set_userdata(sock, this);
remoteAddr = addr;
handleLinkEstablish = [=](utp_socket* usock) {
EnterState(eLinkEstablished);
sock = usock;
};
}
void
Start()
{
}
};
BaseSession::BaseSession()
BaseSession::BaseSession(llarp_router* r)
{
recvBufOffset = 0;
TimedOut = [&](llarp_time_t now) -> bool {
return this->IsTimedOut(now);
};
lastActive = llarp_time_now_ms();
Pump = [&]() { PumpWrite(this->sock); };
Tick = std::bind(&BaseSession::TickImpl, this, std::placeholders::_1);
SendMessageBuffer = std::bind(&BaseSession::QueueWriteBuffers, this,
std::placeholders::_1);
IsEstablished = [&]() { return this->state == eSessionReady; };
HandleLinkIntroMessage = [](const LinkIntroMessage*) -> bool {
return false;
};
}
BaseSession::BaseSession(llarp_router* r, utp_socket* s,
const RouterContact& rc, const AddressInfo& addr)
: BaseSession(r)
{
remoteRC.Clear();
remoteTransportPubKey = addr.pubkey;
remoteRC = rc;
sock = s;
assert(utp_set_userdata(sock, this) == this);
remoteAddr = addr;
Start = std::bind(&BaseSession::Connect, this);
}
BaseSession::BaseSession(llarp_router* r, utp_socket* s, const Addr& addr)
: BaseSession(r)
{
remoteRC.Clear();
sock = s;
assert(utp_set_userdata(sock, this) == this);
remoteAddr = addr;
Start = []() {};
}
llarp_router*
BaseSession::Router()
{
return parent->router;
}
BaseSession::~BaseSession()
{
}
ILinkSession*
std::unique_ptr< ILinkSession >
LinkLayer::NewOutboundSession(const RouterContact& rc,
const AddressInfo& addr)
{
auto router = m_router;
if(router)
return new OutboundSession(router, utp_create_socket(_utp_ctx), rc,
addr);
return nullptr;
return std::make_unique< BaseSession >(
router, utp_create_socket(_utp_ctx), rc, addr);
}
ILinkSession*
LinkLayer::NewInboundSession(const Addr& addr)
uint64
LinkLayer::OnRead(utp_callback_arguments* arg)
{
return nullptr;
BaseSession* self =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
if(self)
{
assert(self->sock);
assert(self->sock == arg->socket);
if(self->state == BaseSession::eSessionReady)
self->Recv(arg->buf, arg->len);
else if(self->state == BaseSession::eLinkEstablished)
{
LinkLayer* parent =
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
self->RecvHandshake(arg->buf, arg->len, parent, arg->socket);
}
utp_read_drained(arg->socket);
}
else
{
llarp::LogWarn("utp_socket got data with no underlying session");
}
return 0;
}
uint64
LinkLayer::OnStateChange(utp_callback_arguments* arg)
{
LinkLayer* l =
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
BaseSession* session =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
if(arg->state == UTP_STATE_CONNECT)
{
assert(session->sock);
assert(session->sock == arg->socket);
session->OutboundLinkEstablished(l);
}
else if(arg->state == UTP_STATE_EOF)
{
session->SendClose();
}
return 0;
}
uint64
@ -614,13 +496,175 @@ namespace llarp
utp_close(arg->socket);
return 0;
}
InboundSession* session =
new InboundSession(self->m_router, arg->socket, remote);
BaseSession* session = new BaseSession(self->router, arg->socket, remote);
self->PutSession(remote, session);
session->LinkEstablished(arg->socket);
session->OnLinkEstablished(self);
return 0;
}
void
BaseSession::EncryptThenHash(FragmentBuffer& buf, const byte_t* ptr,
uint32_t sz, bool isLastFragment)
{
buf.Randomize();
const byte_t* nonce = buf.data() + FragmentHashSize;
byte_t* body = buf.data() + FragmentOverheadSize;
byte_t* base = body;
if(isLastFragment)
htobe32buf(body, 0);
body += sizeof(uint32_t);
htobe32buf(body, sz);
body += sizeof(uint32_t);
memcpy(body, ptr, sz);
auto payload = InitBuffer(base, FragmentBodySize);
parent->router->crypto.xchacha20(payload, sessionKey, nonce);
parent->router->crypto.hmac(buf, payload, sessionKey);
}
void
BaseSession::EnterState(State st)
{
state = st;
lastActive = llarp_time_now_ms();
if(st == eSessionReady)
{
parent->MapAddr(this->remoteAddr, remoteRC.pubkey);
Router()->HandleLinkSessionEstablished(remoteRC);
}
}
bool
BaseSession::VerifyThenDecrypt(FragmentBuffer& buf)
{
ShortHash digest;
if(!Router()->crypto.hmac(
digest,
InitBuffer(buf.data() + FragmentHashSize,
FragmentBufferSize - FragmentHashSize),
sessionKey))
{
llarp::LogError("keyed hash failed");
return false;
}
if(digest != ShortHash(buf.data()))
{
llarp::LogError("Message Integrity Failed");
return false;
}
AlignedBuffer< FragmentNonceSize > nonce(buf.data() + FragmentHashSize);
auto body = InitBuffer(buf.data() + FragmentOverheadSize,
FragmentBufferSize - FragmentOverheadSize);
Router()->crypto.xchacha20(body, sessionKey, nonce);
uint32_t upper, lower;
if(!(llarp_buffer_read_uint32(&body, &upper)
&& llarp_buffer_read_uint32(&body, &lower)))
return false;
bool fragmentEnd = upper == 0;
if(recvMsg.size() + lower > MAX_LINK_MSG_SIZE)
{
llarp::LogError("Fragment too big: ", lower, " bytes");
return false;
}
size_t newsz = recvMsg.size() + lower;
recvMsg.reserve(newsz);
byte_t* ptr = recvMsg.data() + (newsz - lower);
memcpy(ptr, body.cur, lower);
if(fragmentEnd)
{
// got a message
auto mbuf = Buffer(recvMsg);
auto result = Router()->HandleRecvLinkMessageBuffer(this, mbuf);
recvMsg.clear();
recvMsg.shrink_to_fit();
return result;
}
return true;
}
void
BaseSession::RecvHandshake(const void* buf, size_t bufsz, LinkLayer* p,
utp_socket* s)
{
size_t sz = bufsz;
parent = p;
sock = s;
if(parent->HasSessionVia(remoteAddr))
{
llarp::LogDebug("already have session via ", remoteAddr,
" so closing before processing handshake");
Close(sock);
return;
}
llarp::LogDebug("recv handshake ", sz, " from ", remoteAddr);
if(recvBuf.size() < sz)
{
llarp::LogDebug("handshake too big from ", remoteAddr);
Close(sock);
return;
}
if(sz <= 8)
{
llarp::LogDebug("handshake too small from ", remoteAddr);
Close(sock);
return;
}
memcpy(recvBuf.data(), buf, sz);
// process handshake header
uint8_t* ptr = recvBuf.data();
uint32_t version = bufbe32toh(ptr);
if(version != LLARP_PROTO_VERSION)
{
llarp::LogWarn("protocol version missmatch ", version,
" != ", LLARP_PROTO_VERSION);
Close(sock);
return;
}
ptr += sizeof(uint32_t);
sz -= sizeof(uint32_t);
uint32_t limsz = bufbe32toh(ptr);
ptr += sizeof(uint32_t);
sz -= sizeof(uint32_t);
if(limsz > sz)
{
// not enough data
// TODO: don't bail here, continue reading
llarp::LogDebug("not enough data for handshake, want ", limsz,
" bytes but got ", sz);
Close(sock);
return;
}
llarp::LogInfo("read LIM from ", remoteAddr);
// process LIM
auto mbuf = InitBuffer(ptr, limsz);
LinkIntroMessage msg(this);
if(!msg.BDecode(&mbuf))
{
llarp::LogError("Failed to parse LIM from ", remoteAddr);
llarp::DumpBuffer(mbuf);
Close(sock);
return;
}
if(!msg.HandleMessage(Router()))
{
llarp::LogError("failed to verify signature of rc");
return;
}
if(!DoKeyExchange(sock, Router()->crypto.dh_server, msg.N, msg.rc.enckey,
parent->TransportSecretKey()))
return;
remoteRC = msg.rc;
gotLIM = true;
llarp::LogInfo("we got a new session from ", GetPubKey());
EnterState(eSessionReady);
}
} // namespace utp
} // namespace llarp

View File

@ -22,9 +22,20 @@ namespace llarp
return false;
return *strbuf.cur == 'i';
}
if(llarp_buffer_eq(key, "n"))
{
if(N.BDecode(buf))
return true;
llarp::LogWarn("failed to decode nonce in LIM");
return false;
}
if(llarp_buffer_eq(key, "r"))
{
return rc.BDecode(buf);
if(rc.BDecode(buf))
return true;
llarp::LogWarn("failed to decode RC in LIM");
llarp::DumpBuffer(*buf);
return false;
}
else if(llarp_buffer_eq(key, "v"))
{
@ -76,9 +87,6 @@ namespace llarp
bool
LinkIntroMessage::HandleMessage(llarp_router* router) const
{
if(!rc.VerifySignature(&router->crypto))
return false;
router->async_verify_RC(rc, !rc.IsPublicRouter());
return true;
return rc.VerifySignature(&router->crypto);
}
} // namespace llarp

View File

@ -9,8 +9,6 @@ namespace llarp
InboundMessageParser::InboundMessageParser(llarp_router* _router)
: router(_router)
{
reader.user = this;
reader.on_key = &OnKey;
}
bool
@ -18,11 +16,11 @@ namespace llarp
{
InboundMessageParser* handler =
static_cast< InboundMessageParser* >(r->user);
llarp_buffer_t strbuf;
// we are reading the first key
if(handler->firstkey)
{
llarp_buffer_t strbuf;
// check for empty dict
if(!key)
return false;
@ -49,25 +47,27 @@ namespace llarp
switch(*strbuf.cur)
{
case 'i':
handler->msg = new LinkIntroMessage(handler->from);
handler->msg = std::make_unique< LinkIntroMessage >(handler->from);
break;
case 'd':
handler->msg = new RelayDownstreamMessage(handler->from);
handler->msg =
std::make_unique< RelayDownstreamMessage >(handler->from);
break;
case 'u':
handler->msg = new RelayUpstreamMessage(handler->from);
handler->msg =
std::make_unique< RelayUpstreamMessage >(handler->from);
break;
case 'm':
handler->msg = new DHTImmeidateMessage(handler->from);
handler->msg = std::make_unique< DHTImmeidateMessage >(handler->from);
break;
case 'c':
handler->msg = new LR_CommitMessage(handler->from);
handler->msg = std::make_unique< LR_CommitMessage >(handler->from);
break;
default:
return false;
}
handler->firstkey = false;
return handler->msg != nullptr;
return true;
}
// check for last element
if(!key)
@ -83,8 +83,6 @@ namespace llarp
if(msg)
{
result = msg->HandleMessage(router);
delete msg;
msg = nullptr;
}
return result;
}
@ -92,8 +90,16 @@ namespace llarp
bool
InboundMessageParser::ProcessFrom(ILinkSession* src, llarp_buffer_t buf)
{
from = src;
firstkey = true;
reader.user = this;
reader.on_key = &OnKey;
from = src;
firstkey = true;
return bencode_read_dict(&buf, &reader);
}
void
InboundMessageParser::Reset()
{
msg.reset(nullptr);
}
} // namespace llarp

View File

@ -218,10 +218,10 @@ crypto_threadworker_verifyrc(void *user)
{
llarp_async_verify_rc *verify_request =
static_cast< llarp_async_verify_rc * >(user);
verify_request->valid =
verify_request->rc.VerifySignature(verify_request->nodedb->crypto);
llarp::RouterContact rc = verify_request->rc;
verify_request->valid = rc.VerifySignature(verify_request->nodedb->crypto);
// if it's valid we need to set it
if(verify_request->valid && verify_request->rc.IsPublicRouter())
if(verify_request->valid && rc.IsPublicRouter())
{
llarp::LogDebug("RC is valid, saving to disk");
llarp_threadpool_queue_job(verify_request->diskworker,

View File

@ -322,8 +322,9 @@ namespace llarp
{
}
Path::Path(const std::vector< RouterContact >& h) : hops(h.size())
Path::Path(const std::vector< RouterContact >& h)
{
hops.resize(h.size());
size_t hsz = h.size();
for(size_t idx = 0; idx < hsz; ++idx)
{

View File

@ -198,8 +198,7 @@ namespace llarp
{
// select hops
std::vector< RouterContact > hops;
for(size_t i = 0; i < numHops; ++i)
hops.emplace_back();
hops.resize(numHops);
size_t idx = 0;
while(idx < numHops)
{

View File

@ -21,19 +21,20 @@ namespace llarp
struct async_verify_context
{
llarp_router *router;
llarp::OutboundLinkEstablishJob *establish_job;
TryConnectJob *establish_job;
};
} // namespace llarp
struct TryConnectJob : public llarp::OutboundLinkEstablishJob
struct TryConnectJob
{
llarp::RouterContact rc;
llarp::ILinkLayer *link;
llarp_router *router;
uint16_t triesLeft;
TryConnectJob(const llarp::RouterContact &remote, llarp::ILinkLayer *l,
uint16_t tries, llarp_router *r)
: OutboundLinkEstablishJob(remote), link(l), router(r), triesLeft(tries)
: rc(remote), link(l), router(r), triesLeft(tries)
{
}
@ -80,8 +81,7 @@ struct TryConnectJob : public llarp::OutboundLinkEstablishJob
static void
on_try_connecting(void *u)
{
llarp::OutboundLinkEstablishJob *j =
static_cast< llarp::OutboundLinkEstablishJob * >(u);
TryConnectJob *j = static_cast< TryConnectJob * >(u);
j->Attempt();
}
@ -97,27 +97,20 @@ llarp_router_try_connect(struct llarp_router *router,
return false;
}
auto link = router->outboundLink.get();
auto itr = router->pendingEstablishJobs.insert(std::make_pair(
remote.pubkey, new TryConnectJob(remote, link, numretries, router)));
llarp::OutboundLinkEstablishJob *job = itr.first->second.get();
auto link = router->outboundLink.get();
auto itr = router->pendingEstablishJobs.insert(std::make_pair(
remote.pubkey,
std::make_unique< TryConnectJob >(remote, link, numretries, router)));
TryConnectJob *job = itr.first->second.get();
// try establishing async
llarp_logic_queue_job(router->logic, {job, on_try_connecting});
return true;
}
void
llarp_router::HandleLinkSessionEstablished(llarp::RouterID k)
llarp_router::HandleLinkSessionEstablished(const llarp::RouterContact &rc)
{
auto itr = pendingEstablishJobs.find(k);
if(itr != pendingEstablishJobs.end())
{
itr->second->Success();
}
else
{
FlushOutboundFor(k);
}
async_verify_RC(rc);
}
llarp_router::llarp_router()
@ -341,8 +334,11 @@ llarp_router::on_verify_server_rc(llarp_async_verify_rc *job)
llarp::LogDebug("rc verified and saved to nodedb");
// refresh valid routers RC value if it's there
router->validRouters[pk] = job->rc;
if(router->validRouters.count(pk))
{
router->validRouters.erase(pk);
}
router->validRouters.insert(std::make_pair(pk, job->rc));
// track valid router in dht
router->dht->impl.nodes->PutNode(job->rc);
@ -391,7 +387,7 @@ llarp_router::HandleDHTLookupForTryEstablishTo(
const std::vector< llarp::RouterContact > &results)
{
for(const auto &result : results)
async_verify_RC(result, false);
async_verify_RC(result);
}
size_t
@ -568,18 +564,21 @@ llarp_router::GetRandomConnectedRouter(llarp::RouterContact &result) const
}
void
llarp_router::async_verify_RC(const llarp::RouterContact &rc,
bool isExpectingClient,
llarp::OutboundLinkEstablishJob *establish_job)
llarp_router::async_verify_RC(const llarp::RouterContact &rc)
{
llarp_async_verify_rc *job = new llarp_async_verify_rc();
llarp::async_verify_context *ctx = new llarp::async_verify_context();
ctx->router = this;
ctx->establish_job = establish_job;
job->user = ctx;
job->rc = rc;
job->valid = false;
job->hook = nullptr;
ctx->establish_job = nullptr;
auto itr = pendingEstablishJobs.find(rc.pubkey);
if(itr != pendingEstablishJobs.end())
ctx->establish_job = itr->second.get();
job->user = ctx;
job->rc = rc;
job->valid = false;
job->hook = nullptr;
job->nodedb = nodedb;
job->logic = logic;
@ -587,10 +586,10 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc,
job->cryptoworker = tp;
job->diskworker = disk;
if(isExpectingClient)
job->hook = &llarp_router::on_verify_client_rc;
else
if(rc.IsPublicRouter())
job->hook = &llarp_router::on_verify_server_rc;
else
job->hook = &llarp_router::on_verify_client_rc;
llarp_nodedb_async_verify(job);
}

View File

@ -30,6 +30,8 @@ bool
llarp_findOrCreateEncryption(llarp_crypto *crypto, const char *fpath,
llarp::SecretKey &encryption);
struct TryConnectJob;
struct llarp_router
{
bool ready;
@ -95,8 +97,7 @@ struct llarp_router
validRouters;
// pending establishing session with routers
std::unordered_map< llarp::RouterID,
std::unique_ptr< llarp::OutboundLinkEstablishJob >,
std::unordered_map< llarp::RouterID, std::unique_ptr< TryConnectJob >,
llarp::RouterID::Hash >
pendingEstablishJobs;
@ -107,7 +108,8 @@ struct llarp_router
llarp_router();
virtual ~llarp_router();
void HandleLinkSessionEstablished(llarp::PubKey);
void
HandleLinkSessionEstablished(const llarp::RouterContact &);
bool
HandleRecvLinkMessageBuffer(llarp::ILinkSession *from, llarp_buffer_t msg);
@ -217,8 +219,7 @@ struct llarp_router
GetRandomConnectedRouter(llarp::RouterContact &result) const;
void
async_verify_RC(const llarp::RouterContact &rc, bool isExpectingClient,
llarp::OutboundLinkEstablishJob *job = nullptr);
async_verify_RC(const llarp::RouterContact &rc);
void
HandleDHTLookupForSendTo(llarp::RouterID remote,

View File

@ -223,9 +223,13 @@ namespace llarp
RouterContact &
RouterContact::operator=(const RouterContact &other)
{
addrs = other.addrs;
signature = other.signature;
exits = other.exits;
addrs.clear();
exits.clear();
addrs = other.addrs;
exits = other.exits;
signature = other.signature;
last_updated = other.last_updated;
enckey = other.enckey;
pubkey = other.pubkey;

View File

@ -43,7 +43,7 @@
// win32 needs experimental
#include <experimental/filesystem>
#else
#include <filesystem>
#include <experimental/filesystem>
#endif
#else
// OpenBSD needs this