Files
hrynco-kswitcher/tests/lan_clipboard.cpp
T

77 lines
6.0 KiB
C++

#include <winsock2.h>
#include "lan_clipboard.h"
#include <iostream>
using namespace ks;using namespace ks::lan;
int checks{};
void check(bool b,const char *s){if(!b)throw std::runtime_error(s);++checks;}
int main(){try{
const std::string aid(32,'a'),bid(32,'b'),cid(32,'c'),key(64,'d');
ClipboardExchange a(aid),b(bid),c(cid);
a.peerSession(bid,b.session());a.peerSession(cid,c.session());b.peerSession(aid,a.session());c.peerSession(aid,a.session());
Json trusted{{bid,{{"key",key}}},{cid,{{"key",key}}}},back{{aid,{{"key",key}}}};
Bytes payload(13000);for(size_t i=0;i<payload.size();++i)payload[i]=static_cast<unsigned char>(i);
a.queue({bid,cid},payload,"editor.exe",1);
std::deque<std::pair<std::string,Json>> packets,acks;Json firstPacket;int sent=0;
auto output=[&](const std::string &to,const Json &p){check(p.dump().size()<=2048,"datagram fits discovery bound");if(firstPacket.is_null())firstPacket=p;packets.push_back({to,p});};
auto ack=[&](const std::string &,const Json &p){acks.push_back({p.at("id"),p});};
for(uint64_t now=10;now<10000;now+=20){
a.poll(trusted,now,output);
while(!packets.empty()){
auto [to,p]=std::move(packets.front());packets.pop_front();if(++sent%7==0)continue;
auto &receiver=to==bid?b:c;
receiver.receive(aid,key,p,now,123,ack);
receiver.receive(aid,key,p,now,123,ack); // duplicate datagrams do not duplicate history
}
while(!acks.empty()){
auto [from,p]=std::move(acks.front());acks.pop_front();if(++sent%11==0)continue;
a.receive(from,key,p,now,123,output);
}
}
check(b.deliveries.size()==1&&c.deliveries.size()==1,"all paired instances receive exactly once despite loss/duplicates");
check(b.deliveries[0].bytes==payload&&c.deliveries[0].bytes==payload,"binary clipboard survives retransmission");
check(b.deliveries[0].sequence==123&&b.deliveries[0].app=="editor.exe","capture sequence and origin retained");
b.receive(aid,key,firstPacket,10000,123,ack);
check(acks.empty(),"authenticated old packet replay is ignored");
ClipboardExchange restarted(bid);restarted.peerSession(aid,a.session());
restarted.receive(aid,key,firstPacket,10001,123,ack);
check(restarted.deliveries.empty()&&acks.empty(),"old datagrams rejected after recipient restart");
auto damaged=firstPacket;damaged["number"]=1000000;auto data=damaged["data"].get<std::string>();data[0]=data[0]=='0'?'1':'0';damaged["data"]=data;
bool rejected=false;try{b.receive(aid,key,damaged,10002,123,ack);}catch(...){rejected=true;}
check(rejected,"modified ciphertext rejected");
a.queue({bid},Bytes{1,2,3},"second",10003);
a.peerSession(bid,restarted.session());
for(uint64_t now=10004;now<11000;now+=20){
a.poll(trusted,now,output);
while(!packets.empty()){auto p=std::move(packets.front().second);packets.pop_front();restarted.receive(aid,key,p,now,456,ack);}
while(!acks.empty()){auto p=std::move(acks.front().second);acks.pop_front();a.receive(bid,key,p,now,456,output);}
}
check(restarted.deliveries.size()==1&&restarted.deliveries[0].bytes==Bytes({1,2,3}),"clipboard works after peer restart");
bool limited=false;try{a.queue({bid},Bytes(32*1024*1024+1),"large",12000);}catch(...){limited=true;}
check(limited,"oversized clipboard bounded");
{
WSADATA startup{};check(WSAStartup(MAKEWORD(2,2),&startup)==0,"UDP benchmark starts");
SOCKET sa=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP),sb=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
struct Cleanup {SOCKET a,b;~Cleanup(){closesocket(a);closesocket(b);WSACleanup();}} cleanup{sa,sb};
sockaddr_in aa{},ab{};aa.sin_family=ab.sin_family=AF_INET;aa.sin_addr.s_addr=ab.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
check(bind(sa,reinterpret_cast<sockaddr*>(&aa),sizeof(aa))==0&&bind(sb,reinterpret_cast<sockaddr*>(&ab),sizeof(ab))==0,"UDP benchmark binds");
int length=sizeof(aa);getsockname(sa,reinterpret_cast<sockaddr*>(&aa),&length);length=sizeof(ab);getsockname(sb,reinterpret_cast<sockaddr*>(&ab),&length);
for(auto socket:{sa,sb}){u_long nonblock=1;ioctlsocket(socket,FIONBIO,&nonblock);int buffer=1024*1024;setsockopt(socket,SOL_SOCKET,SO_RCVBUF,reinterpret_cast<char*>(&buffer),sizeof(buffer));}
ClipboardExchange sender(aid),receiver(bid);sender.peerSession(bid,receiver.session());receiver.peerSession(aid,sender.session());
Bytes large(2*1024*1024);uint32_t random=123;for(auto &v:large){random=random*1664525+1013904223;v=static_cast<unsigned char>(random>>24);}
auto started=GetTickCount64();sender.queue({bid},large,"benchmark",started);
auto send=[&](SOCKET socket,const sockaddr_in &address,const Json &p){auto data=p.dump();if(sendto(socket,data.data(),int(data.size()),0,reinterpret_cast<const sockaddr*>(&address),sizeof(address))!=int(data.size()))throw std::runtime_error("UDP benchmark send failed");};
while(receiver.deliveries.empty()&&GetTickCount64()-started<15000){
auto now=GetTickCount64();sender.poll(trusted,now,[&](const auto &,const Json &p){send(sa,ab,p);});
bool progressed=false;
for(int n=0;n<64;++n){char buffer[2048];auto size=recv(sb,buffer,sizeof(buffer),0);if(size<=0)break;progressed=true;
receiver.receive(aid,key,Json::parse(buffer,buffer+size),now,123,[&](const auto &,const Json &p){send(sb,aa,p);});}
for(int n=0;n<64;++n){char buffer[2048];auto size=recv(sa,buffer,sizeof(buffer),0);if(size<=0)break;progressed=true;
sender.receive(bid,key,Json::parse(buffer,buffer+size),now,123,[&](const auto &,const Json &p){send(sa,ab,p);});}
if(!progressed)Sleep(1);
}
check(receiver.deliveries.size()==1&&receiver.deliveries[0].bytes==large,"2 MiB crosses actual encrypted UDP sockets intact");
std::cout<<"2 MiB encrypted UDP loopback: "<<(GetTickCount64()-started)<<" ms\n";
}
std::cout<<checks<<" LAN clipboard checks passed\n";return 0;
}catch(const std::exception &e){std::cerr<<e.what()<<'\n';return 1;}}