You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lokinet/llarp/link/utp.cpp

1061 lines
28 KiB
C++

#include <buffer.hpp>
#include <endian.hpp>
#include <link/server.hpp>
#include <link/utp.hpp>
#include <messages/discard.hpp>
#include <messages/link_intro.hpp>
#include <router.hpp>
#include <utp.h>
6 years ago
#include <cassert>
6 years ago
#include <tuple>
6 years ago
#include <deque>
6 years ago
#ifdef __linux__
#include <linux/errqueue.h>
#include <netinet/ip_icmp.h>
#endif
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#endif
#ifndef IP_DONTFRAGMENT
#define IP_DONTFRAGMENT IP_DONTFRAG
#endif
namespace llarp
{
namespace utp
{
6 years ago
constexpr size_t FragmentHashSize = 32;
6 years ago
constexpr size_t FragmentNonceSize = 32;
constexpr size_t FragmentOverheadSize =
FragmentHashSize + FragmentNonceSize;
6 years ago
constexpr size_t FragmentBodyPayloadSize = 1024;
6 years ago
constexpr size_t FragmentBodyOverhead = 32;
6 years ago
constexpr size_t FragmentBodySize =
FragmentBodyOverhead + FragmentBodyPayloadSize;
6 years ago
constexpr size_t FragmentBufferSize =
6 years ago
FragmentOverheadSize + FragmentBodySize;
6 years ago
typedef llarp::AlignedBuffer< FragmentBufferSize > FragmentBuffer;
/// maximum size for send queue for a session before we drop
constexpr size_t MaxSendQueueSize = 1024;
6 years ago
using MessageBuffer = llarp::AlignedBuffer< MAX_LINK_MSG_SIZE >;
6 years ago
struct LinkLayer;
6 years ago
/// pending inbound message being received
struct InboundMessage
{
llarp_time_t lastActive;
MessageBuffer msg;
llarp_buffer_t buffer = llarp::Buffer(msg);
/// return true if this inbound message can be removed due to expiration
bool
IsExpired(llarp_time_t now) const
{
return now > lastActive && now - lastActive >= 2000;
}
};
struct BaseSession : public ILinkSession
{
RouterContact remoteRC;
6 years ago
utp_socket* sock;
LinkLayer* parent;
bool gotLIM;
PubKey remoteTransportPubKey;
Addr remoteAddr;
6 years ago
SharedSecret rxKey;
SharedSecret txKey;
llarp_time_t lastActive;
6 years ago
const static llarp_time_t sessionTimeout = 30 * 1000;
6 years ago
/// send queue for utp
std::deque< utp_iovec > vecq;
6 years ago
/// current fragments waiting to be sent
6 years ago
std::deque< FragmentBuffer > sendq;
6 years ago
6 years ago
/// current fragment buffer
FragmentBuffer recvBuf;
6 years ago
/// current offset in current fragment buffer
6 years ago
size_t recvBufOffset;
6 years ago
/// messages we are recving right now
std::unordered_map< uint32_t, InboundMessage > m_RecvMsgs;
/// are we stalled or nah?
6 years ago
bool stalled = false;
6 years ago
6 years ago
/// mark session as alive
6 years ago
void
6 years ago
Alive();
6 years ago
6 years ago
/// base
BaseSession(LinkLayer* p);
6 years ago
/// outbound
BaseSession(LinkLayer* p, utp_socket* s, const RouterContact& rc,
6 years ago
const AddressInfo& addr);
/// inbound
BaseSession(LinkLayer* p, utp_socket* s, const Addr& remote);
6 years ago
enum State
{
eInitial,
eConnecting,
eLinkEstablished, // when utp connection is established
eCryptoHandshake, // crypto handshake initiated
eSessionReady, // session is ready
eClose // utp connection is closed
};
llarp::Router*
6 years ago
Router();
State state;
6 years ago
/// hook for utp
void
6 years ago
OnLinkEstablished(LinkLayer* p)
{
6 years ago
parent = p;
EnterState(eLinkEstablished);
llarp::LogDebug("link established with ", remoteAddr);
}
6 years ago
void
EnterState(State st);
6 years ago
BaseSession();
~BaseSession();
6 years ago
/// pump outbound send queue
void
6 years ago
PumpWrite()
{
if(!sock)
return;
ssize_t expect = 0;
std::vector< utp_iovec > vecs;
for(const auto& vec : vecq)
{
expect += vec.iov_len;
vecs.push_back(vec);
}
if(expect)
{
ssize_t s = utp_writev(sock, vecs.data(), vecs.size());
llarp::LogDebug("utp_writev wrote=", s, " expect=", expect,
" to=", remoteAddr);
while(s > static_cast< ssize_t >(vecq.front().iov_len))
{
s -= vecq.front().iov_len;
vecq.pop_front();
sendq.pop_front();
}
if(vecq.size())
{
auto& front = vecq.front();
front.iov_len -= s;
front.iov_base = ((byte_t*)front.iov_base) + s;
}
}
}
6 years ago
/// verify a fragment buffer and the decrypt it
bool
6 years ago
VerifyThenDecrypt(byte_t* buf);
6 years ago
/// encrypt a fragment then hash the ciphertext
void
6 years ago
EncryptThenHash(const byte_t* ptr, uint32_t sz, bool isLastFragment);
6 years ago
/// queue a fully formed message
bool
QueueWriteBuffers(llarp_buffer_t buf);
6 years ago
/// do low level connect
6 years ago
void
Connect()
{
utp_connect(sock, remoteAddr, remoteAddr.SockLen());
EnterState(eConnecting);
}
6 years ago
/// handle outbound connection made
6 years ago
void
OutboundLinkEstablished(LinkLayer* p)
{
OnLinkEstablished(p);
OutboundHandshake();
6 years ago
}
// send first message
6 years ago
void
OutboundHandshake();
6 years ago
6 years ago
// do key exchange for handshake
6 years ago
bool
6 years ago
DoKeyExchange(transport_dh_func dh, SharedSecret& K,
const KeyExchangeNonce& n, const PubKey& other,
const byte_t* secret)
{
ShortHash t_h;
AlignedBuffer< 64 > tmp;
6 years ago
memcpy(tmp.data(), K, K.size());
memcpy(tmp.data() + K.size(), n, n.size());
// t_h = HS(K + L.n)
if(!Router()->crypto.shorthash(t_h, ConstBuffer(tmp)))
{
llarp::LogError("failed to mix key to ", remoteAddr);
return false;
}
// K = TKE(a.p, B_a.e, sk, t_h)
6 years ago
if(!dh(K, other, secret, t_h))
{
llarp::LogError("key exchange with ", other, " failed");
6 years ago
return false;
}
llarp::LogDebug("keys mixed with session to ", remoteAddr);
6 years ago
return true;
}
6 years ago
/// does K = HS(K + A)
bool
MutateKey(SharedSecret& K, const AlignedBuffer< 24 >& A)
{
AlignedBuffer< 56 > tmp;
auto buf = llarp::Buffer(tmp);
memcpy(buf.cur, K.data(), K.size());
buf.cur += K.size();
memcpy(buf.cur, A, A.size());
buf.cur = buf.base;
return Router()->crypto.shorthash(K, buf);
}
void
TickImpl(__attribute__((unused)) llarp_time_t now)
{
}
6 years ago
/// close session
void
6 years ago
Close();
6 years ago
/// low level read
bool
Recv(const void* buf, size_t sz)
{
6 years ago
Alive();
byte_t* ptr = (byte_t*)buf;
llarp::LogDebug("utp read ", sz, " from ", remoteAddr);
6 years ago
size_t s = sz;
6 years ago
// process leftovers
if(recvBufOffset)
{
6 years ago
auto left = FragmentBufferSize - recvBufOffset;
6 years ago
if(s >= left)
6 years ago
{
6 years ago
// yes it fills it
6 years ago
llarp::LogDebug("process leftovers, offset=", recvBufOffset,
" sz=", s, " left=", left);
memcpy(recvBuf.data() + recvBufOffset, ptr, left);
s -= left;
recvBufOffset = 0;
ptr += left;
6 years ago
if(!VerifyThenDecrypt(recvBuf.data()))
6 years ago
return false;
}
}
6 years ago
// process full fragments
6 years ago
while(s >= FragmentBufferSize)
{
6 years ago
recvBufOffset = 0;
6 years ago
llarp::LogDebug("process full sz=", s);
if(!VerifyThenDecrypt(ptr))
return false;
ptr += FragmentBufferSize;
s -= FragmentBufferSize;
}
if(s)
{
// hold onto leftovers
llarp::LogDebug("leftovers sz=", s);
6 years ago
memcpy(recvBuf.data() + recvBufOffset, ptr, s);
recvBufOffset += s;
6 years ago
}
return true;
}
bool
InboundLIM(const LinkIntroMessage* msg);
bool
OutboundLIM(const LinkIntroMessage* msg);
bool
6 years ago
IsTimedOut(llarp_time_t now) const
{
if(state == eClose)
return true;
if(now < lastActive)
return false;
6 years ago
auto dlt = now - lastActive;
if(dlt >= sessionTimeout)
{
llarp::LogDebug("session timeout reached for ", remoteAddr);
return true;
}
return false;
}
const PubKey&
6 years ago
RemotePubKey() const
{
return remoteRC.pubkey;
}
const Addr&
6 years ago
RemoteEndpoint() const
{
return remoteAddr;
}
void
MarkEstablished();
6 years ago
}; // namespace utp
struct LinkLayer : public ILinkLayer
{
utp_context* _utp_ctx = nullptr;
llarp::Router* router = nullptr;
static uint64
6 years ago
OnRead(utp_callback_arguments* arg);
static uint64
SendTo(utp_callback_arguments* arg)
{
LinkLayer* l =
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
llarp::LogDebug("utp_sendto ", Addr(*arg->address), " ", arg->len,
6 years ago
" bytes");
// For whatever reason, the UTP_UDP_DONTFRAG flag is set
// on the socket itself....which isn't correct and causes
// winsock (at minimum) to reeee
// here, we check its value, then set fragmentation the _right_
// way. Naturally, Linux has its own special procedure.
// Of course, the flag itself is cleared. -rick
#ifndef _WIN32
6 years ago
// No practical method of doing this on NetBSD or Darwin
// without resorting to raw sockets
#if !(__NetBSD__ || __OpenBSD__ || (__APPLE__ && __MACH__))
#ifndef __linux__
if(arg->flags == 2)
{
int val = 1;
setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val,
sizeof(val));
}
else
{
int val = 0;
setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val,
sizeof(val));
}
#else
if(arg->flags == 2)
{
int val = IP_PMTUDISC_DO;
setsockopt(l->m_udp.fd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
sizeof(val));
}
else
{
int val = IP_PMTUDISC_DONT;
setsockopt(l->m_udp.fd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
sizeof(val));
}
6 years ago
#endif
#endif
arg->flags = 0;
if(::sendto(l->m_udp.fd, (char*)arg->buf, arg->len, arg->flags,
arg->address, arg->address_len)
6 years ago
== -1
&& errno)
#else
if(arg->flags == 2)
6 years ago
{
char val = 1;
setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val,
sizeof(val));
}
else
{
char val = 0;
setsockopt(l->m_udp.fd, IPPROTO_IP, IP_DONTFRAGMENT, &val,
sizeof(val));
}
arg->flags = 0;
if(::sendto(l->m_udp.fd, (char*)arg->buf, arg->len, arg->flags,
arg->address, arg->address_len)
== -1)
#endif
6 years ago
{
#ifdef _WIN32
char buf[1024];
int err = WSAGetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, LANG_NEUTRAL,
buf, 1024, nullptr);
llarp::LogError("sendto failed: ", buf);
#else
6 years ago
llarp::LogError("sendto failed: ", strerror(errno));
#endif
6 years ago
}
return 0;
}
static uint64
OnError(utp_callback_arguments* arg)
{
BaseSession* session =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
if(session)
{
session->Router()->OnConnectTimeout(session->GetPubKey());
llarp::LogError(utp_error_code_names[arg->error_code], " via ",
session->remoteAddr);
session->Close();
}
return 0;
}
static uint64
6 years ago
OnStateChange(utp_callback_arguments*);
static uint64
OnAccept(utp_callback_arguments*);
6 years ago
static uint64
OnLog(utp_callback_arguments* arg)
{
llarp::LogDebug(arg->buf);
return 0;
}
LinkLayer(llarp::Router* r) : ILinkLayer()
{
6 years ago
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);
6 years ago
utp_set_callback(_utp_ctx, UTP_ON_STATE_CHANGE,
&LinkLayer::OnStateChange);
utp_set_callback(_utp_ctx, UTP_ON_READ, &LinkLayer::OnRead);
6 years ago
utp_set_callback(_utp_ctx, UTP_ON_ERROR, &LinkLayer::OnError);
utp_set_callback(_utp_ctx, UTP_LOG, &LinkLayer::OnLog);
6 years ago
utp_context_set_option(_utp_ctx, UTP_LOG_NORMAL, 1);
utp_context_set_option(_utp_ctx, UTP_LOG_MTU, 1);
utp_context_set_option(_utp_ctx, UTP_LOG_DEBUG, 1);
6 years ago
utp_context_set_option(_utp_ctx, UTP_SNDBUF, MAX_LINK_MSG_SIZE * 16);
6 years ago
utp_context_set_option(_utp_ctx, UTP_RCVBUF, MAX_LINK_MSG_SIZE * 64);
}
~LinkLayer()
{
utp_destroy(_utp_ctx);
}
uint16_t
Rank() const
{
return 1;
}
void
RecvFrom(const Addr& from, const void* buf, size_t sz)
{
utp_process_udp(_utp_ctx, (const byte_t*)buf, sz, from, from.SockLen());
}
6 years ago
#ifdef __linux__
void
ProcessICMP()
{
do
{
byte_t vec_buf[4096], ancillary_buf[4096];
struct iovec iov = {vec_buf, sizeof(vec_buf)};
struct sockaddr_in remote;
struct msghdr msg;
ssize_t len;
struct cmsghdr* cmsg;
struct sock_extended_err* e;
struct sockaddr* icmp_addr;
struct sockaddr_in* icmp_sin;
memset(&msg, 0, sizeof(msg));
6 years ago
msg.msg_name = &remote;
msg.msg_namelen = sizeof(remote);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = ancillary_buf;
6 years ago
msg.msg_controllen = sizeof(ancillary_buf);
len = recvmsg(m_udp.fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
if(len < 0)
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
errno = 0;
else
llarp::LogError("failed to read icmp for utp ", strerror(errno));
return;
}
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if(cmsg->cmsg_type != IP_RECVERR)
{
continue;
}
if(cmsg->cmsg_level != SOL_IP)
{
continue;
}
e = (struct sock_extended_err*)CMSG_DATA(cmsg);
if(!e)
continue;
if(e->ee_origin != SO_EE_ORIGIN_ICMP)
{
continue;
}
icmp_addr = (struct sockaddr*)SO_EE_OFFENDER(e);
6 years ago
icmp_sin = (struct sockaddr_in*)icmp_addr;
6 years ago
if(icmp_sin->sin_port != 0)
{
continue;
}
if(e->ee_type == 3 && e->ee_code == 4)
{
utp_process_icmp_fragmentation(_utp_ctx, vec_buf, len,
(struct sockaddr*)&remote,
sizeof(remote), e->ee_info);
}
else
{
utp_process_icmp_error(_utp_ctx, vec_buf, len,
(struct sockaddr*)&remote, sizeof(remote));
}
}
} while(true);
}
#endif
void
Pump()
{
6 years ago
utp_issue_deferred_acks(_utp_ctx);
6 years ago
#ifdef __linux__
ProcessICMP();
#endif
std::set< RouterID > sessions;
{
Lock l(m_AuthedLinksMutex);
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
sessions.insert(itr->first);
++itr;
}
}
6 years ago
ILinkLayer::Pump();
{
Lock l(m_AuthedLinksMutex);
for(const auto& pk : sessions)
{
if(m_AuthedLinks.count(pk) == 0)
{
// all sessions were removed
router->SessionClosed(pk);
}
}
}
6 years ago
}
void
Stop()
{
}
llarp::Router*
6 years ago
GetRouter();
bool
KeyGen(SecretKey& k)
{
6 years ago
router->crypto.encryption_keygen(k);
return true;
}
6 years ago
void
Tick(llarp_time_t now)
{
utp_check_timeouts(_utp_ctx);
ILinkLayer::Tick(now);
}
6 years ago
ILinkSession*
NewOutboundSession(const RouterContact& rc, const AddressInfo& addr);
utp_socket*
NewSocket()
{
return utp_create_socket(_utp_ctx);
}
const char*
Name() const
{
return "utp";
}
};
std::unique_ptr< ILinkLayer >
NewServer(llarp::Router* r)
{
6 years ago
return std::unique_ptr< LinkLayer >(new LinkLayer(r));
}
6 years ago
/// base constructor
BaseSession::BaseSession(LinkLayer* p)
{
parent = p;
6 years ago
remoteTransportPubKey.Zero();
SendQueueBacklog = [&]() -> size_t { return sendq.size(); };
6 years ago
SendKeepAlive = [&]() -> bool {
auto now = parent->now();
if(sendq.size() == 0 && state == eSessionReady && now > lastActive
&& now - lastActive > 5000)
6 years ago
{
DiscardMessage msg;
byte_t tmp[128] = {0};
6 years ago
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
6 years ago
if(!msg.BEncode(&buf))
return false;
6 years ago
buf.sz = buf.cur - buf.base;
6 years ago
buf.cur = buf.base;
if(!this->QueueWriteBuffers(buf))
return false;
}
return true;
};
6 years ago
gotLIM = false;
6 years ago
recvBufOffset = 0;
6 years ago
TimedOut = [&](llarp_time_t now) -> bool {
6 years ago
return this->IsTimedOut(now) || this->state == eClose;
6 years ago
};
6 years ago
GetPubKey = std::bind(&BaseSession::RemotePubKey, this);
lastActive = parent->now();
6 years ago
Pump = std::bind(&BaseSession::PumpWrite, this);
6 years ago
Tick = std::bind(&BaseSession::TickImpl, this, std::placeholders::_1);
6 years ago
SendMessageBuffer = std::bind(&BaseSession::QueueWriteBuffers, this,
std::placeholders::_1);
IsEstablished = [=]() {
return this->state == eSessionReady || this->state == eLinkEstablished;
6 years ago
};
6 years ago
6 years ago
SendClose = std::bind(&BaseSession::Close, this);
6 years ago
GetRemoteEndpoint = std::bind(&BaseSession::RemoteEndpoint, this);
6 years ago
}
6 years ago
/// outbound session
BaseSession::BaseSession(LinkLayer* p, utp_socket* s,
6 years ago
const RouterContact& rc, const AddressInfo& addr)
: BaseSession(p)
{
6 years ago
p->router->crypto.shorthash(txKey, InitBuffer(rc.pubkey, PUBKEYSIZE));
6 years ago
remoteRC.Clear();
remoteTransportPubKey = addr.pubkey;
6 years ago
remoteRC = rc;
sock = s;
6 years ago
assert(utp_set_userdata(sock, this) == this);
6 years ago
assert(s == sock);
6 years ago
remoteAddr = addr;
6 years ago
Start = std::bind(&BaseSession::Connect, this);
GotLIM =
std::bind(&BaseSession::OutboundLIM, this, std::placeholders::_1);
6 years ago
}
6 years ago
/// inbound session
BaseSession::BaseSession(LinkLayer* p, utp_socket* s, const Addr& addr)
: BaseSession(p)
6 years ago
{
6 years ago
p->router->crypto.shorthash(rxKey,
InitBuffer(p->router->pubkey(), PUBKEYSIZE));
6 years ago
remoteRC.Clear();
sock = s;
6 years ago
assert(s == sock);
6 years ago
assert(utp_set_userdata(sock, this) == this);
remoteAddr = addr;
6 years ago
Start = []() {};
GotLIM = std::bind(&BaseSession::InboundLIM, this, std::placeholders::_1);
}
bool
BaseSession::InboundLIM(const LinkIntroMessage* msg)
{
if(gotLIM && remoteRC.pubkey != msg->rc.pubkey)
{
return false;
}
6 years ago
if(!gotLIM)
{
remoteRC = msg->rc;
gotLIM = true;
if(!DoKeyExchange(Router()->crypto.transport_dh_server, txKey, msg->N,
remoteRC.enckey, parent->TransportSecretKey()))
return false;
}
EnterState(eSessionReady);
return true;
}
bool
BaseSession::QueueWriteBuffers(llarp_buffer_t buf)
{
if(sendq.size() >= MaxSendQueueSize)
return false;
llarp::LogDebug("write ", buf.sz, " bytes to ", remoteAddr);
6 years ago
lastActive = parent->now();
size_t sz = buf.sz;
byte_t* ptr = buf.base;
while(sz)
{
uint32_t s = std::min(FragmentBodyPayloadSize, sz);
EncryptThenHash(ptr, s, ((sz - s) == 0));
ptr += s;
sz -= s;
}
return true;
}
bool
BaseSession::OutboundLIM(const LinkIntroMessage* msg)
{
if(gotLIM && remoteRC.pubkey != msg->rc.pubkey)
{
return false;
}
remoteRC = msg->rc;
6 years ago
gotLIM = true;
return DoKeyExchange(Router()->crypto.transport_dh_client, msg->N,
remoteTransportPubKey, Router()->encryption);
}
void
BaseSession::OutboundHandshake()
{
byte_t tmp[LinkIntroMessage::MaxSize];
auto buf = StackBuffer< decltype(tmp) >(tmp);
// build our RC
LinkIntroMessage msg;
msg.rc = Router()->rc();
if(!msg.rc.Verify(&Router()->crypto))
{
llarp::LogError("our RC is invalid? closing session to", remoteAddr);
Close();
return;
}
msg.N.Randomize();
msg.P = DefaultLinkSessionLifetime;
if(!msg.Sign(&Router()->crypto, Router()->identity))
{
llarp::LogError("failed to sign LIM for outbound handshake to ",
remoteAddr);
Close();
return;
}
// encode
if(!msg.BEncode(&buf))
{
llarp::LogError("failed to encode LIM for handshake to ", remoteAddr);
Close();
return;
}
// rewind
6 years ago
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// send
if(!SendMessageBuffer(buf))
{
llarp::LogError("failed to send handshake to ", remoteAddr);
Close();
return;
}
6 years ago
if(!DoKeyExchange(Router()->crypto.transport_dh_client, txKey, msg.N,
remoteTransportPubKey, Router()->encryption))
{
llarp::LogError("failed to mix keys for outbound session to ",
remoteAddr);
Close();
return;
}
EnterState(eSessionReady);
6 years ago
}
llarp::Router*
6 years ago
BaseSession::Router()
{
6 years ago
return parent->router;
}
BaseSession::~BaseSession()
{
6 years ago
if(sock)
{
utp_shutdown(sock, SHUT_RDWR);
utp_close(sock);
6 years ago
utp_set_userdata(sock, nullptr);
}
}
6 years ago
ILinkSession*
LinkLayer::NewOutboundSession(const RouterContact& rc,
const AddressInfo& addr)
{
return new BaseSession(this, utp_create_socket(_utp_ctx), rc, addr);
6 years ago
}
uint64
LinkLayer::OnRead(utp_callback_arguments* arg)
{
BaseSession* self =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
6 years ago
6 years ago
if(self)
{
6 years ago
if(self->state == BaseSession::eClose)
{
return 0;
}
if(!self->Recv(arg->buf, arg->len))
6 years ago
{
llarp::LogDebug("recv fail for ", self->remoteAddr);
self->Close();
return 0;
6 years ago
}
utp_read_drained(arg->socket);
6 years ago
}
else
{
llarp::LogWarn("utp_socket got data with no underlying session");
utp_close(arg->socket);
6 years ago
}
return 0;
}
6 years ago
uint64
LinkLayer::OnStateChange(utp_callback_arguments* arg)
{
6 years ago
LinkLayer* l =
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
BaseSession* session =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
6 years ago
if(session)
6 years ago
{
6 years ago
if(arg->state == UTP_STATE_CONNECT)
{
if(session->state == BaseSession::eClose)
{
return 0;
}
session->OutboundLinkEstablished(l);
}
6 years ago
else if(arg->state == UTP_STATE_WRITABLE)
{
session->PumpWrite();
6 years ago
}
6 years ago
else if(arg->state == UTP_STATE_EOF)
{
6 years ago
llarp::LogDebug("got eof from ", session->remoteAddr);
session->Close();
6 years ago
}
6 years ago
}
return 0;
}
uint64
LinkLayer::OnAccept(utp_callback_arguments* arg)
{
LinkLayer* self =
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
Addr remote(*arg->address);
llarp::LogDebug("utp accepted from ", remote);
BaseSession* session = new BaseSession(self, arg->socket, remote);
self->PutSession(session);
6 years ago
session->OnLinkEstablished(self);
return 0;
}
6 years ago
void
6 years ago
BaseSession::EncryptThenHash(const byte_t* ptr, uint32_t msgid,
uint16_t length, uint16_t remaining)
6 years ago
{
sendq.emplace_back();
auto& buf = sendq.back();
vecq.emplace_back();
6 years ago
auto& vec = vecq.back();
vec.iov_base = buf.data();
6 years ago
vec.iov_len = FragmentBufferSize;
6 years ago
llarp::LogDebug("encrypt then hash ", sz, " bytes last=", isLastFragment);
6 years ago
buf.Randomize();
6 years ago
AlignedBuffer< 24 > A = buf.data();
byte_t* nonce = buf.data() + FragmentHashSize;
byte_t* body = nonce + FragmentNonceSize;
byte_t* base = body;
6 years ago
if(isLastFragment)
htobe32buf(body, 0);
6 years ago
else
htobe32buf(body, 1);
6 years ago
body += sizeof(uint32_t);
htobe32buf(body, sz);
body += sizeof(uint32_t);
memcpy(body, ptr, sz);
6 years ago
auto payload =
InitBuffer(base, FragmentBufferSize - FragmentOverheadSize);
// encrypt
6 years ago
Router()->crypto.xchacha20(payload, txKey, nonce);
6 years ago
payload.base = nonce;
6 years ago
payload.cur = payload.base;
payload.sz = FragmentBufferSize - FragmentHashSize;
6 years ago
// key'd hash
6 years ago
Router()->crypto.hmac(buf.data(), payload, txKey);
6 years ago
}
void
BaseSession::EnterState(State st)
{
6 years ago
state = st;
Alive();
6 years ago
if(st == eSessionReady)
{
6 years ago
parent->MapAddr(remoteRC.pubkey, this);
Router()->HandleLinkSessionEstablished(remoteRC, parent);
6 years ago
}
}
bool
6 years ago
BaseSession::VerifyThenDecrypt(byte_t* buf)
6 years ago
{
6 years ago
llarp::LogDebug("verify then decrypt ", remoteAddr);
6 years ago
ShortHash digest;
6 years ago
6 years ago
auto hbuf = InitBuffer(buf + FragmentHashSize,
FragmentBufferSize - FragmentHashSize);
6 years ago
if(!Router()->crypto.hmac(digest.data(), hbuf, rxKey))
6 years ago
{
llarp::LogError("keyed hash failed");
return false;
}
6 years ago
ShortHash expected(buf);
if(expected != digest)
6 years ago
{
6 years ago
llarp::LogError("Message Integrity Failed: got ", digest, " from ",
6 years ago
remoteAddr, " instead of ", expected);
llarp::DumpBuffer(InitBuffer(buf, FragmentBufferSize));
6 years ago
return false;
}
6 years ago
auto body = InitBuffer(buf + FragmentOverheadSize,
6 years ago
FragmentBufferSize - FragmentOverheadSize);
6 years ago
Router()->crypto.xchacha20(body, rxKey, buf + FragmentHashSize);
6 years ago
6 years ago
AlignedBuffer< 24 > A = body.cur;
MutateKey(rxKey, A);
body.cur += 24;
uint32_t msgid;
if(!llarp_buffer_read_uint32(&body, &msgid))
return false;
uint32_t length, remaining;
if(!(llarp_buffer_read_uint16(&body, &length)
&& llarp_buffer_read_uint16(&body, &remaining)))
6 years ago
return false;
6 years ago
if(length > (body.sz - (body.cur - body.base)))
6 years ago
{
6 years ago
// too big length
6 years ago
return false;
}
6 years ago
auto& inbound = m_RecvMsgs[msgid];
inbound.lastActive = Router()->Now();
inbound.FeedData(body.cur, length);
if(remaining == 0)
6 years ago
{
6 years ago
// reszie
inbound.buffer.sz = inbound.buffer.cur - inbound.buffer.base;
// rewind
inbound.buffer.cur = inbound.buffer.base;
// process
if(!Router()->HandleRecvLinkMessageBuffer(this, inbound.buffer))
return false;
6 years ago
}
return true;
}
6 years ago
void
6 years ago
BaseSession::Close()
6 years ago
{
if(state != eClose)
{
6 years ago
if(sock)
{
utp_shutdown(sock, SHUT_RDWR);
utp_close(sock);
llarp::LogDebug("utp_close ", remoteAddr);
utp_set_userdata(sock, nullptr);
}
6 years ago
}
EnterState(eClose);
sock = nullptr;
}
void
BaseSession::Alive()
{
lastActive = parent->now();
6 years ago
}
} // namespace utp
} // namespace llarp