mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
325 lines
8.6 KiB
C++
325 lines
8.6 KiB
C++
/*
|
|
* RetroShare Friend Server
|
|
* Copyright (C) 2021-2021 retroshare team <retroshare.project@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef WINDOWS_SYS
|
|
#include <windows.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#else
|
|
#include <netinet/tcp.h>
|
|
#endif
|
|
|
|
#include "util/rsnet.h"
|
|
#include "util/rsprint.h"
|
|
#include "util/rsdebug.h"
|
|
|
|
#include "pqi/pqithreadstreamer.h"
|
|
#include "pqi/pqifdbin.h"
|
|
|
|
#include "network.h"
|
|
#include "friend_server/fsitem.h"
|
|
|
|
FsNetworkInterface::FsNetworkInterface(const std::string& listening_address,uint16_t listening_port)
|
|
: PQInterface(RsPeerId()),mFsNiMtx(std::string("FsNetworkInterface")),mListeningAddress(listening_address),mListeningPort(listening_port)
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
|
|
mClintListn = 0;
|
|
mClintListn = socket(AF_INET, SOCK_STREAM, 0); // creating socket
|
|
|
|
int flags=1;
|
|
setsockopt(mClintListn,SOL_SOCKET,TCP_NODELAY,(char*)&flags,sizeof(flags));
|
|
|
|
unix_fcntl_nonblock(mClintListn);
|
|
|
|
struct sockaddr_in ipOfServer;
|
|
memset(&ipOfServer, '0', sizeof(ipOfServer));
|
|
|
|
assert(mListeningPort > 1024);
|
|
|
|
ipOfServer.sin_family = AF_INET;
|
|
ipOfServer.sin_port = htons(mListeningPort); // this is the port number of running server
|
|
|
|
int addr[4];
|
|
if(sscanf(listening_address.c_str(),"%d.%d.%d.%d",&addr[0],&addr[1],&addr[2],&addr[3]) != 4)
|
|
throw std::runtime_error("Cannot parse a proper IPv4 address in \""+listening_address+"\"");
|
|
|
|
for(int i=0;i<4;++i)
|
|
if(addr[i] < 0 || addr[i] > 255)
|
|
throw std::runtime_error("Cannot parse a proper IPv4 address in \""+listening_address+"\"");
|
|
|
|
ipOfServer.sin_addr.s_addr = htonl( (addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3] );
|
|
|
|
if(bind(mClintListn, (struct sockaddr*)&ipOfServer , sizeof(ipOfServer)) < 0)
|
|
{
|
|
RsErr() << "Error while binding: errno=" << errno ;
|
|
return;
|
|
}
|
|
|
|
if(listen(mClintListn , 40) < 0)
|
|
{
|
|
RsErr() << "Error while calling listen: errno=" << errno ;
|
|
return;
|
|
}
|
|
|
|
RsDbg() << "Network interface now listening for TCP on " << sockaddr_storage_tostring( *(sockaddr_storage*)&ipOfServer) ;
|
|
}
|
|
|
|
FsNetworkInterface::~FsNetworkInterface()
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
for(auto& it:mConnections)
|
|
{
|
|
delete it.second.pqi_thread;
|
|
std::cerr << "Releasing socket " << it.second.socket << std::endl;
|
|
close(it.second.socket);
|
|
}
|
|
std::cerr << "Releasing listening socket " << mClintListn << std::endl;
|
|
close(mClintListn);
|
|
}
|
|
void FsNetworkInterface::threadTick()
|
|
{
|
|
// 1 - check for new connections
|
|
|
|
checkForNewConnections();
|
|
|
|
// 2 - tick all streamers
|
|
|
|
std::list<RsPeerId> to_close;
|
|
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
for(auto& it:mConnections)
|
|
{
|
|
it.second.pqi_thread->tick();
|
|
|
|
if(!it.second.bio->isactive() && !it.second.bio->moretoread(0))
|
|
to_close.push_back(it.first);
|
|
}
|
|
|
|
for(const auto& pid:to_close)
|
|
locked_closeConnection(pid);
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
}
|
|
|
|
static RsPeerId makePeerId(int t)
|
|
{
|
|
unsigned char s[RsPeerId::SIZE_IN_BYTES];
|
|
memset(s,0,sizeof(s));
|
|
|
|
*reinterpret_cast<int*>(&s) = t;
|
|
return RsPeerId::fromBufferUnsafe(s);
|
|
}
|
|
bool FsNetworkInterface::checkForNewConnections()
|
|
{
|
|
// look for incoming data
|
|
|
|
struct sockaddr addr;
|
|
socklen_t addr_len = sizeof(sockaddr);
|
|
|
|
int clintConnt = accept(mClintListn, &addr, &addr_len); // accept is a blocking call!
|
|
|
|
if(clintConnt < 0)
|
|
{
|
|
int err = rs_socket_error();
|
|
|
|
if(err == EWOULDBLOCK || err == EAGAIN)
|
|
;//RsErr()<< "Incoming connection with nothing to read!" << std::endl;
|
|
else
|
|
RsErr()<< "Error when accepting connection." << std::endl;
|
|
|
|
return false;
|
|
}
|
|
RsDbg() << "Got incoming connection from " << sockaddr_storage_tostring( *(sockaddr_storage*)&addr);
|
|
|
|
// Make the socket non blocking so that we can read from it and return if nothing comes
|
|
|
|
int flags=1;
|
|
setsockopt(clintConnt,SOL_SOCKET,TCP_NODELAY,(char*)&flags,sizeof(flags));
|
|
|
|
unix_fcntl_nonblock(clintConnt);
|
|
|
|
// Create connection info
|
|
|
|
RsDbg() << " Creating connection data." ;
|
|
|
|
ConnectionData c;
|
|
c.socket = clintConnt;
|
|
c.client_address = addr;
|
|
RsPeerId pid = makePeerId(clintConnt);
|
|
|
|
RsDbg() << " socket: " << clintConnt;
|
|
RsDbg() << " client address: " << sockaddr_storage_tostring(*(sockaddr_storage*)&addr);
|
|
RsDbg() << " peer id: " << pid ;
|
|
|
|
// Setup a pqistreamer to deserialize whatever comes from this connection
|
|
|
|
RsSerialiser *rss = new RsSerialiser ;
|
|
rss->addSerialType(new FsSerializer) ;
|
|
|
|
RsFdBinInterface *bio = new RsFdBinInterface(clintConnt,true);
|
|
|
|
auto pqi = new pqithreadstreamer(this,rss, pid, bio,BIN_FLAGS_READABLE | BIN_FLAGS_WRITEABLE);
|
|
|
|
c.pqi_thread = pqi;
|
|
c.bio = bio;
|
|
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
mConnections[pid] = c;
|
|
|
|
pqi->start();
|
|
}
|
|
|
|
RsDbg() << " streamer has properly started." ;
|
|
return true;
|
|
}
|
|
|
|
bool FsNetworkInterface::RecvItem(RsItem *item)
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
|
|
RsDbg() << "FsNetworkInterface: received item " << (void*)item;
|
|
|
|
auto it = mConnections.find(item->PeerId());
|
|
|
|
if(it == mConnections.end())
|
|
{
|
|
RsErr() << "Receiving an item for peer ID " << item->PeerId() << " but no connection is known for that peer." << std::endl;
|
|
delete item;
|
|
return false;
|
|
}
|
|
|
|
it->second.incoming_items.push_back(item);
|
|
return true;
|
|
}
|
|
|
|
RsItem *FsNetworkInterface::GetItem()
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
|
|
for(auto& it:mConnections)
|
|
{
|
|
if(!it.second.incoming_items.empty())
|
|
{
|
|
RsItem *item = it.second.incoming_items.front();
|
|
it.second.incoming_items.pop_front();
|
|
|
|
RsDbg() << "FsNetworkInterface: returning item " << (void*)item << " to caller.";
|
|
return item;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int FsNetworkInterface::SendItem(RsItem *item)
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
|
|
const auto& it = mConnections.find(item->PeerId());
|
|
|
|
if(it == mConnections.end())
|
|
{
|
|
RsErr() << "Cannot send item to peer " << item->PeerId() << ": no pending sockets available." ;
|
|
delete item;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t ss;
|
|
return it->second.pqi_thread->SendItem(item,ss);
|
|
}
|
|
|
|
void FsNetworkInterface::closeConnection(const RsPeerId& peer_id)
|
|
{
|
|
RS_STACK_MUTEX(mFsNiMtx);
|
|
|
|
locked_closeConnection(peer_id);
|
|
}
|
|
void FsNetworkInterface::locked_closeConnection(const RsPeerId& peer_id)
|
|
{
|
|
RsDbg() << "Closing connection to virtual peer " << peer_id ;
|
|
|
|
const auto& it = mConnections.find(peer_id);
|
|
|
|
if(it == mConnections.end())
|
|
{
|
|
RsErr() << " Cannot close connection to peer " << peer_id << ": no pending sockets available." ;
|
|
return;
|
|
}
|
|
|
|
if(!it->second.incoming_items.empty())
|
|
{
|
|
RsErr() << " Trying to close an incoming connection with incoming items still pending! The items will be lost:" << std::endl;
|
|
|
|
for(auto& item:it->second.incoming_items)
|
|
{
|
|
RsErr() << *item;
|
|
delete item;
|
|
}
|
|
|
|
it->second.incoming_items.clear();
|
|
}
|
|
// Close the socket and delete everything.
|
|
|
|
close(it->second.socket);
|
|
it->second.pqi_thread->fullstop();
|
|
it->second.bio->close();
|
|
|
|
delete it->second.pqi_thread;
|
|
|
|
mConnections.erase(it);
|
|
}
|
|
|
|
void FsNetworkInterface::debugPrint()
|
|
{
|
|
RsDbg() << " " << mClintListn ; // listening socket
|
|
RsDbg() << " Connections: " << mConnections.size() ;
|
|
|
|
for(auto& it:mConnections)
|
|
RsDbg() << " " << it.first << ": from \"" << sockaddr_storage_tostring(*(sockaddr_storage*)(&it.second.client_address)) << "\", socket=" << it.second.socket ;
|
|
|
|
std::map<RsPeerId,ConnectionData> mConnections;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|