mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-24 23:19:29 -05:00
commit
caf64cd000
@ -347,6 +347,7 @@ list(
|
|||||||
util/rsurl.cc
|
util/rsurl.cc
|
||||||
util/folderiterator.cc
|
util/folderiterator.cc
|
||||||
util/rsdir.cc
|
util/rsdir.cc
|
||||||
|
util/rsfile.cc
|
||||||
util/dnsresolver.cc
|
util/dnsresolver.cc
|
||||||
util/extaddrfinder.cc
|
util/extaddrfinder.cc
|
||||||
util/rsdebug.cc
|
util/rsdebug.cc
|
||||||
|
@ -9,13 +9,15 @@ libretroshare_shared {
|
|||||||
} else {
|
} else {
|
||||||
CONFIG += staticlib
|
CONFIG += staticlib
|
||||||
}
|
}
|
||||||
CONFIG -= qt
|
|
||||||
TARGET = retroshare
|
TARGET = retroshare
|
||||||
TARGET_PRL = libretroshare
|
TARGET_PRL = libretroshare
|
||||||
DESTDIR = lib
|
DESTDIR = lib
|
||||||
|
|
||||||
!include("use_libretroshare.pri"):error("Including")
|
!include("use_libretroshare.pri"):error("Including")
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS += -fPIC
|
||||||
|
|
||||||
# treat warnings as error for better removing
|
# treat warnings as error for better removing
|
||||||
#QMAKE_CFLAGS += -Werror
|
#QMAKE_CFLAGS += -Werror
|
||||||
#QMAKE_CXXFLAGS += -Werror
|
#QMAKE_CXXFLAGS += -Werror
|
||||||
@ -365,6 +367,8 @@ HEADERS += pqi/authssl.h \
|
|||||||
pgp/pgphandler.h \
|
pgp/pgphandler.h \
|
||||||
pgp/openpgpsdkhandler.h \
|
pgp/openpgpsdkhandler.h \
|
||||||
pgp/pgpkeyutil.h \
|
pgp/pgpkeyutil.h \
|
||||||
|
pqi/pqifdbin.h \
|
||||||
|
pqi/rstcpsocket.h \
|
||||||
pgp/rscertificate.h \
|
pgp/rscertificate.h \
|
||||||
pgp/pgpauxutils.h \
|
pgp/pgpauxutils.h \
|
||||||
pqi/p3cfgmgr.h \
|
pqi/p3cfgmgr.h \
|
||||||
@ -441,7 +445,7 @@ HEADERS += rsitems/rsitem.h \
|
|||||||
serialiser/rstlvkeyvalue.h \
|
serialiser/rstlvkeyvalue.h \
|
||||||
serialiser/rstlvgenericparam.h \
|
serialiser/rstlvgenericparam.h \
|
||||||
serialiser/rstlvgenericmap.h \
|
serialiser/rstlvgenericmap.h \
|
||||||
serialiser/rstlvgenericmap.inl \
|
serialiser/rstlvgenericmap.inl \
|
||||||
serialiser/rstlvlist.h \
|
serialiser/rstlvlist.h \
|
||||||
serialiser/rstlvmaps.h \
|
serialiser/rstlvmaps.h \
|
||||||
serialiser/rstlvbanlist.h \
|
serialiser/rstlvbanlist.h \
|
||||||
@ -483,6 +487,7 @@ HEADERS += util/folderiterator.h \
|
|||||||
util/rsmemory.h \
|
util/rsmemory.h \
|
||||||
util/smallobject.h \
|
util/smallobject.h \
|
||||||
util/rsdir.h \
|
util/rsdir.h \
|
||||||
|
util/rsfile.h \
|
||||||
util/argstream.h \
|
util/argstream.h \
|
||||||
util/rsdiscspace.h \
|
util/rsdiscspace.h \
|
||||||
util/rsnet.h \
|
util/rsnet.h \
|
||||||
@ -543,6 +548,8 @@ SOURCES += pqi/authgpg.cc \
|
|||||||
pqi/p3cfgmgr.cc \
|
pqi/p3cfgmgr.cc \
|
||||||
pqi/p3peermgr.cc \
|
pqi/p3peermgr.cc \
|
||||||
pqi/p3linkmgr.cc \
|
pqi/p3linkmgr.cc \
|
||||||
|
pqi/pqifdbin.cc \
|
||||||
|
pqi/rstcpsocket.cc \
|
||||||
pqi/p3netmgr.cc \
|
pqi/p3netmgr.cc \
|
||||||
pqi/p3notify.cc \
|
pqi/p3notify.cc \
|
||||||
pqi/pqiqos.cc \
|
pqi/pqiqos.cc \
|
||||||
@ -635,6 +642,7 @@ SOURCES += util/folderiterator.cc \
|
|||||||
util/rsexpr.cc \
|
util/rsexpr.cc \
|
||||||
util/smallobject.cc \
|
util/smallobject.cc \
|
||||||
util/rsdir.cc \
|
util/rsdir.cc \
|
||||||
|
util/rsfile.cc \
|
||||||
util/rsdiscspace.cc \
|
util/rsdiscspace.cc \
|
||||||
util/rsnet.cc \
|
util/rsnet.cc \
|
||||||
util/rsnet_ss.cc \
|
util/rsnet_ss.cc \
|
||||||
@ -716,6 +724,41 @@ SOURCES += rsitems/rsnxsitems.cc \
|
|||||||
gxs/rsgxsrequesttypes.cc \
|
gxs/rsgxsrequesttypes.cc \
|
||||||
gxs/rsnxsobserver.cpp
|
gxs/rsnxsobserver.cpp
|
||||||
|
|
||||||
|
# Tor
|
||||||
|
HEADERS += retroshare/rstor.h
|
||||||
|
|
||||||
|
HEADERS += tor/AddOnionCommand.h \
|
||||||
|
tor/AuthenticateCommand.h \
|
||||||
|
tor/CryptoKey.h \
|
||||||
|
tor/GetConfCommand.h \
|
||||||
|
tor/HiddenService.h \
|
||||||
|
tor/PendingOperation.h \
|
||||||
|
tor/ProtocolInfoCommand.h \
|
||||||
|
tor/TorTypes.h \
|
||||||
|
tor/SetConfCommand.h \
|
||||||
|
tor/StrUtil.h \
|
||||||
|
tor/bytearray.h \
|
||||||
|
tor/TorControl.h \
|
||||||
|
tor/TorControlCommand.h \
|
||||||
|
tor/TorControlSocket.h \
|
||||||
|
tor/TorManager.h \
|
||||||
|
tor/TorProcess.h
|
||||||
|
|
||||||
|
SOURCES += tor/AddOnionCommand.cpp \
|
||||||
|
tor/AuthenticateCommand.cpp \
|
||||||
|
tor/GetConfCommand.cpp \
|
||||||
|
tor/HiddenService.cpp \
|
||||||
|
tor/ProtocolInfoCommand.cpp \
|
||||||
|
tor/SetConfCommand.cpp \
|
||||||
|
tor/TorControlCommand.cpp \
|
||||||
|
tor/TorControl.cpp \
|
||||||
|
tor/TorControlSocket.cpp \
|
||||||
|
tor/TorManager.cpp \
|
||||||
|
tor/TorProcess.cpp \
|
||||||
|
tor/CryptoKey.cpp \
|
||||||
|
tor/PendingOperation.cpp \
|
||||||
|
tor/StrUtil.cpp
|
||||||
|
|
||||||
# gxs tunnels
|
# gxs tunnels
|
||||||
HEADERS += gxstunnel/p3gxstunnel.h \
|
HEADERS += gxstunnel/p3gxstunnel.h \
|
||||||
gxstunnel/rsgxstunnelitems.h \
|
gxstunnel/rsgxstunnelitems.h \
|
||||||
|
@ -161,6 +161,8 @@ OpenPGPSDKHandler::OpenPGPSDKHandler(const std::string& pubring, const std::stri
|
|||||||
++i ;
|
++i ;
|
||||||
}
|
}
|
||||||
_pubring_last_update_time = time(NULL) ;
|
_pubring_last_update_time = time(NULL) ;
|
||||||
|
_pubring_changed = false;
|
||||||
|
|
||||||
RsErr() << "Pubring read successfully." ;
|
RsErr() << "Pubring read successfully." ;
|
||||||
|
|
||||||
if(secring_exist)
|
if(secring_exist)
|
||||||
|
341
libretroshare/src/pqi/pqifdbin.cc
Normal file
341
libretroshare/src/pqi/pqifdbin.cc
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* libretroshare/src/file_sharing: fsbio.cc *
|
||||||
|
* *
|
||||||
|
* libretroshare: retroshare core library *
|
||||||
|
* *
|
||||||
|
* Copyright 2021 by 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 Lesser 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 Lesser General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License *
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "util/rsprint.h"
|
||||||
|
#include "util/rsfile.h"
|
||||||
|
#include "pqi/pqifdbin.h"
|
||||||
|
|
||||||
|
RsFdBinInterface::RsFdBinInterface(int file_descriptor, bool is_socket)
|
||||||
|
: mCLintConnt(file_descriptor),mIsSocket(is_socket),mIsActive(false)
|
||||||
|
{
|
||||||
|
mTotalReadBytes=0;
|
||||||
|
mTotalInBufferBytes=0;
|
||||||
|
mTotalWrittenBytes=0;
|
||||||
|
mTotalOutBufferBytes=0;
|
||||||
|
|
||||||
|
if(file_descriptor!=0)
|
||||||
|
setSocket(file_descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsFdBinInterface::setSocket(int s)
|
||||||
|
{
|
||||||
|
if(mIsActive != 0)
|
||||||
|
{
|
||||||
|
RsErr() << "Changing socket to active FsBioInterface! Canceling all pending R/W data." ;
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
#ifndef WINDOWS_SYS
|
||||||
|
int flags = fcntl(s,F_GETFL);
|
||||||
|
|
||||||
|
if(!(flags & O_NONBLOCK))
|
||||||
|
{
|
||||||
|
RsWarn() << "Trying to use a blocking file descriptor in RsFdBinInterface. This is not going to work! Setting the socket to be non blocking.";
|
||||||
|
unix_fcntl_nonblock(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// On windows, there is no way to determine whether a socket is blocking or not, so we set it to non blocking whatsoever.
|
||||||
|
if (mIsSocket) {
|
||||||
|
unix_fcntl_nonblock(s);
|
||||||
|
} else {
|
||||||
|
RsFileUtil::set_fd_nonblock(s);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mCLintConnt = s;
|
||||||
|
mIsActive = (s!=0);
|
||||||
|
}
|
||||||
|
int RsFdBinInterface::tick()
|
||||||
|
{
|
||||||
|
if(!mIsActive)
|
||||||
|
{
|
||||||
|
RsErr() << "Ticking a non active FsBioInterface!" ;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 2 - read incoming data pending on existing connections
|
||||||
|
|
||||||
|
int res=0;
|
||||||
|
|
||||||
|
res += read_pending();
|
||||||
|
res += write_pending();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsFdBinInterface::read_pending()
|
||||||
|
{
|
||||||
|
char inBuffer[1025];
|
||||||
|
memset(inBuffer,0,1025);
|
||||||
|
|
||||||
|
ssize_t readbytes;
|
||||||
|
#if WINDOWS_SYS
|
||||||
|
if (mIsSocket)
|
||||||
|
// Windows needs recv for sockets
|
||||||
|
readbytes = recv(mCLintConnt, inBuffer, sizeof(inBuffer), 0);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
readbytes = read(mCLintConnt, inBuffer, sizeof(inBuffer)); // Needs read instead of recv which is only for sockets.
|
||||||
|
// Sockets should be set to non blocking by the client process.
|
||||||
|
|
||||||
|
if(readbytes == 0)
|
||||||
|
{
|
||||||
|
RsDbg() << "Reached END of the stream!" ;
|
||||||
|
RsDbg() << "Closing!" ;
|
||||||
|
|
||||||
|
close();
|
||||||
|
return mTotalInBufferBytes;
|
||||||
|
}
|
||||||
|
if(readbytes < 0)
|
||||||
|
{
|
||||||
|
if(errno != 0 && errno != EWOULDBLOCK && errno != EAGAIN)
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
// A non blocking read to file descriptor gets ERROR_NO_DATA for empty data
|
||||||
|
if (mIsSocket == true || GetLastError() != ERROR_NO_DATA)
|
||||||
|
#endif
|
||||||
|
RsErr() << "read() failed. Errno=" << errno ;
|
||||||
|
|
||||||
|
return mTotalInBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_FS_BIN
|
||||||
|
RsDbg() << "clintConnt: " << mCLintConnt << ", readbytes: " << readbytes ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// display some debug info
|
||||||
|
|
||||||
|
if(readbytes > 0)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_FS_BIN
|
||||||
|
RsDbg() << "Received the following bytes: " << RsUtil::BinToHex( reinterpret_cast<unsigned char*>(inBuffer),readbytes,50) << std::endl;
|
||||||
|
RsDbg() << "Received the following bytes: " << std::string(inBuffer,readbytes) << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *ptr = malloc(readbytes);
|
||||||
|
|
||||||
|
if(!ptr)
|
||||||
|
throw std::runtime_error("Cannot allocate memory! Go buy some RAM!");
|
||||||
|
|
||||||
|
memcpy(ptr,inBuffer,readbytes);
|
||||||
|
|
||||||
|
in_buffer.push_back(std::make_pair(ptr,readbytes));
|
||||||
|
mTotalInBufferBytes += readbytes;
|
||||||
|
mTotalReadBytes += readbytes;
|
||||||
|
|
||||||
|
#ifdef DEBUG_FS_BIN
|
||||||
|
RsDbg() << "Socket: " << mCLintConnt << ". Total read: " << mTotalReadBytes << ". Buffer size: " << mTotalInBufferBytes ;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return mTotalInBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsFdBinInterface::write_pending()
|
||||||
|
{
|
||||||
|
if(out_buffer.empty())
|
||||||
|
return mTotalOutBufferBytes;
|
||||||
|
|
||||||
|
auto& p = out_buffer.front();
|
||||||
|
int written;
|
||||||
|
#if WINDOWS_SYS
|
||||||
|
if (mIsSocket)
|
||||||
|
// Windows needs send for sockets
|
||||||
|
written = send(mCLintConnt, (char*) p.first, p.second, 0);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
written = write(mCLintConnt, p.first, p.second);
|
||||||
|
|
||||||
|
if(written < 0)
|
||||||
|
{
|
||||||
|
if(errno != EWOULDBLOCK && errno != EAGAIN)
|
||||||
|
RsErr() << "write() failed. Errno=" << errno ;
|
||||||
|
|
||||||
|
return mTotalOutBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(written == 0)
|
||||||
|
{
|
||||||
|
RsErr() << "write() failed. Nothing sent.";
|
||||||
|
return mTotalOutBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_FS_BIN
|
||||||
|
RsDbg() << "clintConnt: " << mCLintConnt << ", written: " << written ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// display some debug info
|
||||||
|
|
||||||
|
#ifdef DEBUG_FS_BIN
|
||||||
|
RsDbg() << "Sent the following bytes: " << RsUtil::BinToHex( reinterpret_cast<unsigned char*>(p.first),written,50) << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(written < p.second)
|
||||||
|
{
|
||||||
|
void *ptr = malloc(p.second - written);
|
||||||
|
|
||||||
|
if(!ptr)
|
||||||
|
throw std::runtime_error("Cannot allocate memory! Go buy some RAM!");
|
||||||
|
|
||||||
|
memcpy(ptr,static_cast<unsigned char *>(p.first) + written,p.second - written);
|
||||||
|
free(p.first);
|
||||||
|
|
||||||
|
out_buffer.front().first = ptr;
|
||||||
|
out_buffer.front().second = p.second - written;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(p.first);
|
||||||
|
out_buffer.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
mTotalOutBufferBytes -= written;
|
||||||
|
mTotalWrittenBytes += written;
|
||||||
|
|
||||||
|
return mTotalOutBufferBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
RsFdBinInterface::~RsFdBinInterface()
|
||||||
|
{
|
||||||
|
clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsFdBinInterface::clean()
|
||||||
|
{
|
||||||
|
for(auto p:in_buffer) free(p.first);
|
||||||
|
for(auto p:out_buffer) free(p.first);
|
||||||
|
|
||||||
|
in_buffer.clear();
|
||||||
|
out_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsFdBinInterface::readline(void *data, int len)
|
||||||
|
{
|
||||||
|
int n=0;
|
||||||
|
|
||||||
|
for(auto p:in_buffer)
|
||||||
|
for(int i=0;i<p.second;++i,++n)
|
||||||
|
if((n+1==len) || static_cast<unsigned char*>(p.first)[i] == '\n')
|
||||||
|
return readdata(data,n+1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int RsFdBinInterface::readdata(void *data, int len)
|
||||||
|
{
|
||||||
|
// read incoming bytes in the buffer
|
||||||
|
|
||||||
|
int total_len = 0;
|
||||||
|
|
||||||
|
while(total_len < len)
|
||||||
|
{
|
||||||
|
if(in_buffer.empty())
|
||||||
|
{
|
||||||
|
mTotalInBufferBytes -= total_len;
|
||||||
|
return total_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the remaining buffer is too large, chop of the beginning of it.
|
||||||
|
|
||||||
|
if(total_len + in_buffer.front().second > len)
|
||||||
|
{
|
||||||
|
memcpy(&(static_cast<unsigned char *>(data)[total_len]),in_buffer.front().first,len - total_len);
|
||||||
|
|
||||||
|
void *ptr = malloc(in_buffer.front().second - (len - total_len));
|
||||||
|
memcpy(ptr,&(static_cast<unsigned char*>(in_buffer.front().first)[len - total_len]),in_buffer.front().second - (len - total_len));
|
||||||
|
|
||||||
|
free(in_buffer.front().first);
|
||||||
|
in_buffer.front().first = ptr;
|
||||||
|
in_buffer.front().second -= len-total_len;
|
||||||
|
|
||||||
|
mTotalInBufferBytes -= len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
else // copy everything
|
||||||
|
{
|
||||||
|
memcpy(&(static_cast<unsigned char *>(data)[total_len]),in_buffer.front().first,in_buffer.front().second);
|
||||||
|
|
||||||
|
total_len += in_buffer.front().second;
|
||||||
|
|
||||||
|
free(in_buffer.front().first);
|
||||||
|
in_buffer.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTotalInBufferBytes -= len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsFdBinInterface::senddata(void *data, int len)
|
||||||
|
{
|
||||||
|
// shouldn't we better send in multiple packets, similarly to how we read?
|
||||||
|
|
||||||
|
if(len == 0)
|
||||||
|
{
|
||||||
|
RsErr() << "Calling FsBioInterface::senddata() with null size or null data pointer";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void *ptr = malloc(len);
|
||||||
|
|
||||||
|
if(!ptr)
|
||||||
|
{
|
||||||
|
RsErr() << "Cannot allocate data of size " << len ;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ptr,data,len);
|
||||||
|
out_buffer.push_back(std::make_pair(ptr,len));
|
||||||
|
|
||||||
|
mTotalOutBufferBytes += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
int RsFdBinInterface::netstatus()
|
||||||
|
{
|
||||||
|
return mIsActive; // dummy response.
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsFdBinInterface::isactive()
|
||||||
|
{
|
||||||
|
return mIsActive ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsFdBinInterface::moretoread(uint32_t /* usec */)
|
||||||
|
{
|
||||||
|
return mTotalInBufferBytes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsFdBinInterface::moretowrite(uint32_t /* usec */)
|
||||||
|
{
|
||||||
|
return mTotalOutBufferBytes > 0 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsFdBinInterface::cansend(uint32_t)
|
||||||
|
{
|
||||||
|
return isactive();
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsFdBinInterface::close()
|
||||||
|
{
|
||||||
|
RsDbg() << "Stopping network interface" << std::endl;
|
||||||
|
mIsActive = false;
|
||||||
|
mCLintConnt = 0;
|
||||||
|
clean();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
83
libretroshare/src/pqi/pqifdbin.h
Normal file
83
libretroshare/src/pqi/pqifdbin.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* libretroshare/src/file_sharing: fsbio.h *
|
||||||
|
* *
|
||||||
|
* libretroshare: retroshare core library *
|
||||||
|
* *
|
||||||
|
* Copyright 2021 by 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 Lesser 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 Lesser General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License *
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "pqi/pqi_base.h"
|
||||||
|
|
||||||
|
class RsFdBinInterface: public BinInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RsFdBinInterface(int file_descriptor, bool is_socket);
|
||||||
|
~RsFdBinInterface();
|
||||||
|
|
||||||
|
// Implements BinInterface methods
|
||||||
|
|
||||||
|
int tick() override;
|
||||||
|
|
||||||
|
// Schedule data to be sent at the next tick(). The caller keeps memory ownership.
|
||||||
|
//
|
||||||
|
int senddata(void *data, int len) override;
|
||||||
|
|
||||||
|
// Obtains new data from the interface. "data" needs to be initialized for room
|
||||||
|
// to len bytes. The returned value is the actual size of what was read.
|
||||||
|
//
|
||||||
|
int readdata(void *data, int len) override;
|
||||||
|
|
||||||
|
// Read at most len bytes only if \n is encountered within that range. Otherwise, nothing is changed.
|
||||||
|
//
|
||||||
|
int readline(void *data, int len) ;
|
||||||
|
|
||||||
|
int netstatus() override;
|
||||||
|
int isactive() override;
|
||||||
|
bool moretoread(uint32_t usec) override;
|
||||||
|
bool moretowrite(uint32_t usec) ;
|
||||||
|
bool cansend(uint32_t usec) override;
|
||||||
|
|
||||||
|
int close() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If hashing data
|
||||||
|
**/
|
||||||
|
RsFileHash gethash() override { return RsFileHash() ; }
|
||||||
|
uint64_t bytecount() override { return mTotalReadBytes; }
|
||||||
|
|
||||||
|
bool bandwidthLimited() override { return false; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setSocket(int s);
|
||||||
|
void clean();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int read_pending();
|
||||||
|
int write_pending();
|
||||||
|
|
||||||
|
int mCLintConnt;
|
||||||
|
bool mIsSocket;
|
||||||
|
bool mIsActive;
|
||||||
|
uint32_t mTotalReadBytes;
|
||||||
|
uint32_t mTotalInBufferBytes;
|
||||||
|
uint32_t mTotalWrittenBytes;
|
||||||
|
uint32_t mTotalOutBufferBytes;
|
||||||
|
|
||||||
|
std::list<std::pair<void *,int> > in_buffer;
|
||||||
|
std::list<std::pair<void *,int> > out_buffer;
|
||||||
|
};
|
||||||
|
|
100
libretroshare/src/pqi/rstcpsocket.cc
Normal file
100
libretroshare/src/pqi/rstcpsocket.cc
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/******************************* BEGIN WINDOWS/UNIX SPECIFIC PART ******************/
|
||||||
|
#ifndef WINDOWS_SYS
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#else
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
// Missing defines in MinGW
|
||||||
|
#ifndef MSG_WAITALL
|
||||||
|
#define MSG_WAITALL 8
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
/********************************* END WINDOWS/UNIX SPECIFIC PART ******************/
|
||||||
|
#include <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "rstcpsocket.h"
|
||||||
|
|
||||||
|
RsTcpSocket::RsTcpSocket(const std::string& tcp_address,uint16_t tcp_port)
|
||||||
|
:RsFdBinInterface(0, true),mState(DISCONNECTED),mConnectAddress(tcp_address),mConnectPort(tcp_port),mSocket(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RsTcpSocket::RsTcpSocket()
|
||||||
|
:RsFdBinInterface(0, true),mState(DISCONNECTED),mConnectAddress("0.0.0.0"),mConnectPort(0),mSocket(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsTcpSocket::connect(const std::string& tcp_address,uint16_t tcp_port)
|
||||||
|
{
|
||||||
|
if(mState == CONNECTED)
|
||||||
|
close();
|
||||||
|
|
||||||
|
mConnectPort = tcp_port;
|
||||||
|
mConnectAddress = tcp_address;
|
||||||
|
|
||||||
|
return connect();
|
||||||
|
}
|
||||||
|
bool RsTcpSocket::connect()
|
||||||
|
{
|
||||||
|
int CreateSocket = 0;
|
||||||
|
char dataReceived[1024];
|
||||||
|
struct sockaddr_in ipOfServer;
|
||||||
|
|
||||||
|
memset(dataReceived, '0' ,sizeof(dataReceived));
|
||||||
|
|
||||||
|
if((CreateSocket = socket(AF_INET, SOCK_STREAM, 0))< 0)
|
||||||
|
{
|
||||||
|
printf("Socket not created \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ipOfServer.sin_family = AF_INET;
|
||||||
|
ipOfServer.sin_port = htons(mConnectPort);
|
||||||
|
ipOfServer.sin_addr.s_addr = inet_addr(mConnectAddress.c_str());
|
||||||
|
|
||||||
|
if(::connect(CreateSocket, (struct sockaddr *)&ipOfServer, sizeof(ipOfServer))<0)
|
||||||
|
{
|
||||||
|
printf("Connection failed due to port and ip problems, or server is not available. Socket=%d,ConnectPort=%d,ConnectAddress=%s Errno=%d\n",mSocket,mConnectPort,mConnectAddress.c_str(),errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mState = CONNECTED;
|
||||||
|
|
||||||
|
unix_fcntl_nonblock(CreateSocket);
|
||||||
|
setSocket(CreateSocket);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsTcpSocket::close()
|
||||||
|
{
|
||||||
|
RsFdBinInterface::close();
|
||||||
|
mState = DISCONNECTED;
|
||||||
|
|
||||||
|
return !::close(mSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
RsThreadedTcpSocket::RsThreadedTcpSocket(const std::string& tcp_address,uint16_t tcp_port)
|
||||||
|
: RsTcpSocket(tcp_address,tcp_port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
RsThreadedTcpSocket::RsThreadedTcpSocket() : RsTcpSocket()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void RsThreadedTcpSocket::run()
|
||||||
|
{
|
||||||
|
while(!shouldStop() && connectionState() == CONNECTED)
|
||||||
|
{
|
||||||
|
tick();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
}
|
||||||
|
RsWarn() << "Connection to " << connectAddress() << ":" << connectPort() << " is now closed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
RsThreadedTcpSocket::~RsThreadedTcpSocket()
|
||||||
|
{
|
||||||
|
fullstop(); // fully wait for stopping.
|
||||||
|
|
||||||
|
close();
|
||||||
|
}
|
46
libretroshare/src/pqi/rstcpsocket.h
Normal file
46
libretroshare/src/pqi/rstcpsocket.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include <string>
|
||||||
|
#include "util/rsthreads.h"
|
||||||
|
#include "pqi/pqifdbin.h"
|
||||||
|
|
||||||
|
class RsTcpSocket: public RsFdBinInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RsTcpSocket(const std::string& tcp_address,uint16_t tcp_port);
|
||||||
|
RsTcpSocket();
|
||||||
|
virtual ~RsTcpSocket()=default;
|
||||||
|
|
||||||
|
enum State: uint8_t {
|
||||||
|
UNKNOWN = 0x00,
|
||||||
|
DISCONNECTED = 0x01,
|
||||||
|
CONNECTED = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return true when OK, false otherwise.
|
||||||
|
bool connect();
|
||||||
|
|
||||||
|
bool connect(const std::string& tcp_address,uint16_t tcp_port);
|
||||||
|
|
||||||
|
// Returns 1 when OK, 0 otherwise.
|
||||||
|
int close();
|
||||||
|
|
||||||
|
State connectionState() const { return mState; }
|
||||||
|
const std::string& connectAddress() const { return mConnectAddress ; }
|
||||||
|
uint16_t connectPort() const { return mConnectPort ; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
State mState;
|
||||||
|
std::string mConnectAddress;
|
||||||
|
uint16_t mConnectPort;
|
||||||
|
int mSocket;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RsThreadedTcpSocket: public RsTcpSocket, public RsThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RsThreadedTcpSocket(const std::string& tcp_address,uint16_t tcp_port);
|
||||||
|
RsThreadedTcpSocket();
|
||||||
|
virtual ~RsThreadedTcpSocket();
|
||||||
|
|
||||||
|
virtual void run() override;
|
||||||
|
};
|
||||||
|
|
@ -109,7 +109,10 @@ enum class RsEventType : uint32_t
|
|||||||
/** Emitted to update library clients about file hashing being completed */
|
/** Emitted to update library clients about file hashing being completed */
|
||||||
FILE_HASHING_COMPLETED = 20,
|
FILE_HASHING_COMPLETED = 20,
|
||||||
|
|
||||||
__MAX /// Used internally, keep last
|
/// @see rspeers.h
|
||||||
|
TOR_MANAGER = 21,
|
||||||
|
|
||||||
|
__MAX /// Used internally, keep last
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RsEventsErrorNum : int32_t
|
enum class RsEventsErrorNum : int32_t
|
||||||
|
@ -159,6 +159,7 @@ public:
|
|||||||
ERR_ALREADY_RUNNING, /// Another istance is running already
|
ERR_ALREADY_RUNNING, /// Another istance is running already
|
||||||
ERR_CANT_ACQUIRE_LOCK, /// Another istance is already running?
|
ERR_CANT_ACQUIRE_LOCK, /// Another istance is already running?
|
||||||
ERR_NO_AVAILABLE_ACCOUNT, /// Used in retroshare-service -U list when no account is available
|
ERR_NO_AVAILABLE_ACCOUNT, /// Used in retroshare-service -U list when no account is available
|
||||||
|
ERR_CANNOT_CONFIGURE_TOR, /// cannot start/configure Tor for an auto-tor node
|
||||||
ERR_UNKNOWN /// Unkown error, maybe password is wrong?
|
ERR_UNKNOWN /// Unkown error, maybe password is wrong?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -184,6 +185,7 @@ public:
|
|||||||
static bool isPortable();
|
static bool isPortable();
|
||||||
static bool isWindowsXP();
|
static bool isWindowsXP();
|
||||||
static bool collectEntropy(uint32_t bytes) ;
|
static bool collectEntropy(uint32_t bytes) ;
|
||||||
|
static bool startAutoTor();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief lockFilePath
|
* \brief lockFilePath
|
||||||
@ -218,6 +220,7 @@ public:
|
|||||||
static void setAutoLogin(bool autoLogin);
|
static void setAutoLogin(bool autoLogin);
|
||||||
static bool RsClearAutoLogin() ;
|
static bool RsClearAutoLogin() ;
|
||||||
|
|
||||||
|
static std::string executablePath() ;
|
||||||
private:
|
private:
|
||||||
/** @brief Lock profile directory
|
/** @brief Lock profile directory
|
||||||
* param[in] accountDir account directory to lock
|
* param[in] accountDir account directory to lock
|
||||||
|
182
libretroshare/src/retroshare/rstor.h
Normal file
182
libretroshare/src/retroshare/rstor.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This code stands as an interface for the automatic configuration
|
||||||
|
// of Tor hidden services to be used by retroshare.
|
||||||
|
//
|
||||||
|
// The correct way to use it is to:
|
||||||
|
//
|
||||||
|
// 1 - properly set data and hidden service directories. This allowd the TorManager
|
||||||
|
// to save its keys for the hidden service, or to load one that has previously been created
|
||||||
|
//
|
||||||
|
// 2 - call setupHiddenService(). This creates/loads the hidden service.
|
||||||
|
//
|
||||||
|
// 3 - call RsTor::start()
|
||||||
|
//
|
||||||
|
// 4 - loop/wait until RsTor::getHiddenServiceStatus(service_id)
|
||||||
|
// returns RsTorHiddenServiceStatus::ONLINE
|
||||||
|
//
|
||||||
|
// 5 - call RsTor::getHiddenserviceInfo to properly setup RS internal ports and addresses:
|
||||||
|
//
|
||||||
|
// RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port);
|
||||||
|
// RsTor::getProxyServerInfo(proxy_server_address,proxy_server_port) ;
|
||||||
|
//
|
||||||
|
// rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port);
|
||||||
|
// rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port);
|
||||||
|
// rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ;
|
||||||
|
|
||||||
|
#include "retroshare/rsevents.h"
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
class TorManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RsTorManagerEventCode: uint8_t
|
||||||
|
{
|
||||||
|
UNKNOWN = 0x00,
|
||||||
|
TOR_STATUS_CHANGED = 0x01,
|
||||||
|
BOOTSTRAP_STATUS_CHANGED = 0x02,
|
||||||
|
TOR_CONNECTIVITY_CHANGED = 0x03,
|
||||||
|
TOR_MANAGER_ERROR = 0x04,
|
||||||
|
CONFIGURATION_NEEDED = 0x05,
|
||||||
|
TOR_MANAGER_STOPPED = 0x06,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Status of the Tor hidden service setup/loaded by RS
|
||||||
|
|
||||||
|
enum class RsTorHiddenServiceStatus: uint8_t {
|
||||||
|
ERROR = 0x00,
|
||||||
|
NOT_CREATED = 0x01,
|
||||||
|
OFFLINE = 0x02,
|
||||||
|
ONLINE = 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
// Status of the connection/authentication between RS and the Tor service
|
||||||
|
|
||||||
|
enum class RsTorConnectivityStatus: uint8_t {
|
||||||
|
ERROR = 0x00,
|
||||||
|
NOT_CONNECTED = 0x01,
|
||||||
|
CONNECTING = 0x02,
|
||||||
|
SOCKET_CONNECTED = 0x03,
|
||||||
|
AUTHENTICATING = 0x04,
|
||||||
|
AUTHENTICATED = 0x05,
|
||||||
|
HIDDEN_SERVICE_READY = 0x06,
|
||||||
|
UNKNOWN = 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
// Status of the Tor service with which RS is talking.
|
||||||
|
|
||||||
|
enum class RsTorStatus: uint8_t {
|
||||||
|
UNKNOWN = 0x00,
|
||||||
|
OFFLINE = 0x01,
|
||||||
|
READY = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RsTorManagerEvent: public RsEvent
|
||||||
|
{
|
||||||
|
RsTorManagerEvent(): RsEvent(RsEventType::TOR_MANAGER),
|
||||||
|
mTorManagerEventType(RsTorManagerEventCode::UNKNOWN)
|
||||||
|
{}
|
||||||
|
|
||||||
|
RsTorManagerEventCode mTorManagerEventType;
|
||||||
|
|
||||||
|
RsTorConnectivityStatus mTorConnectivityStatus;
|
||||||
|
RsTorStatus mTorStatus;
|
||||||
|
std::string mErrorMessage;
|
||||||
|
|
||||||
|
///* @see RsEvent @see RsSerializable
|
||||||
|
void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override
|
||||||
|
{
|
||||||
|
RsEvent::serial_process(j, ctx);
|
||||||
|
RS_SERIAL_PROCESS(mTorManagerEventType);
|
||||||
|
RS_SERIAL_PROCESS(mTorConnectivityStatus);
|
||||||
|
RS_SERIAL_PROCESS(mTorStatus);
|
||||||
|
RS_SERIAL_PROCESS(mErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RsTorManagerEvent() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RsTor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* \brief isTorAvailable
|
||||||
|
* \return true if a Tor executble has been found. False otherwise.
|
||||||
|
*/
|
||||||
|
static bool isTorAvailable() ;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief torStatus
|
||||||
|
* \return Status of the Tor service used by RS
|
||||||
|
*/
|
||||||
|
static RsTorStatus torStatus() ;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief torConnectivityStatus
|
||||||
|
* \return Status of the connectivity/authentication between RS and Tor
|
||||||
|
*/
|
||||||
|
static RsTorConnectivityStatus torConnectivityStatus() ;
|
||||||
|
|
||||||
|
static void setTorDataDirectory(const std::string& dir);
|
||||||
|
static void setHiddenServiceDirectory(const std::string& dir);
|
||||||
|
|
||||||
|
static bool setupHiddenService();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief getProxyServerInfo
|
||||||
|
* \param server_address Address of the proxy used by RS to send data in the Tor network. Usually 127.0.0.1
|
||||||
|
* \param server_port Port of the proxy used by RS to send data in the Tor network. Usually 9050.
|
||||||
|
*/
|
||||||
|
static void getProxyServerInfo(std::string& server_address, uint16_t& server_port);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief getHiddenServiceStatus
|
||||||
|
* \param service_id onion address of the hidden service (if the service is OFFLINE or ONLINE)
|
||||||
|
* \return Status of the created/loaded hidden service.
|
||||||
|
*/
|
||||||
|
static RsTorHiddenServiceStatus getHiddenServiceStatus(std::string& service_id);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief start
|
||||||
|
* Launches the Tor management threads.
|
||||||
|
*/
|
||||||
|
static bool start();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief stop
|
||||||
|
* Stop the Tor management threads.
|
||||||
|
*/
|
||||||
|
static void stop();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief getHiddenServiceInfo
|
||||||
|
* Gets information about the hidden service setup by RS to run.
|
||||||
|
* \param service_id
|
||||||
|
* \param service_onion_address
|
||||||
|
* \param service_port
|
||||||
|
* \param service_target_address
|
||||||
|
* \param target_port
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
static bool getHiddenServiceInfo(std::string& service_id,
|
||||||
|
std::string& service_onion_address,
|
||||||
|
uint16_t& service_port,
|
||||||
|
std::string& service_target_address,
|
||||||
|
uint16_t& target_port);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief bootstrapStatus
|
||||||
|
* \return Log messages of the Tor bootstrapping status.
|
||||||
|
*/
|
||||||
|
static std::map<std::string,std::string> bootstrapStatus();
|
||||||
|
static std::list<std::string> logMessages();
|
||||||
|
|
||||||
|
static std::string socksAddress();
|
||||||
|
static uint16_t socksPort();
|
||||||
|
|
||||||
|
static bool hasError();
|
||||||
|
static std::string errorMessage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Tor::TorManager *instance();
|
||||||
|
};
|
@ -44,6 +44,7 @@
|
|||||||
#include "util/folderiterator.h"
|
#include "util/folderiterator.h"
|
||||||
#include "util/rsstring.h"
|
#include "util/rsstring.h"
|
||||||
#include "retroshare/rsinit.h"
|
#include "retroshare/rsinit.h"
|
||||||
|
#include "retroshare/rstor.h"
|
||||||
#include "retroshare/rsnotify.h"
|
#include "retroshare/rsnotify.h"
|
||||||
#include "retroshare/rsiface.h"
|
#include "retroshare/rsiface.h"
|
||||||
#include "plugins/pluginmanager.h"
|
#include "plugins/pluginmanager.h"
|
||||||
@ -144,6 +145,7 @@ struct RsInitConfig
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
RsFileHash main_executable_hash;
|
RsFileHash main_executable_hash;
|
||||||
|
std::string mainExecutablePath;
|
||||||
|
|
||||||
#ifdef WINDOWS_SYS
|
#ifdef WINDOWS_SYS
|
||||||
bool portable;
|
bool portable;
|
||||||
@ -305,6 +307,7 @@ int RsInit::InitRetroShare(const RsConfigOptions& conf)
|
|||||||
rsInitConfig->optBaseDir = conf.optBaseDir;
|
rsInitConfig->optBaseDir = conf.optBaseDir;
|
||||||
rsInitConfig->jsonApiPort = conf.jsonApiPort;
|
rsInitConfig->jsonApiPort = conf.jsonApiPort;
|
||||||
rsInitConfig->jsonApiBindAddress = conf.jsonApiBindAddress;
|
rsInitConfig->jsonApiBindAddress = conf.jsonApiBindAddress;
|
||||||
|
rsInitConfig->mainExecutablePath = conf.main_executable_path;
|
||||||
|
|
||||||
#ifdef PTW32_STATIC_LIB
|
#ifdef PTW32_STATIC_LIB
|
||||||
// for static PThreads under windows... we need to init the library...
|
// for static PThreads under windows... we need to init the library...
|
||||||
@ -1931,6 +1934,52 @@ int RsServer::StartupRetroShare()
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string RsInit::executablePath()
|
||||||
|
{
|
||||||
|
return rsInitConfig->mainExecutablePath;
|
||||||
|
}
|
||||||
|
bool RsInit::startAutoTor()
|
||||||
|
{
|
||||||
|
std::cerr << "(II) node is an automated Tor node => launching Tor auto-configuration." << std::endl;
|
||||||
|
// Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service
|
||||||
|
|
||||||
|
std::string tor_hidden_service_dir = RsAccounts::AccountDirectory() + "/hidden_service/" ;
|
||||||
|
|
||||||
|
RsTor::setTorDataDirectory(RsAccounts::ConfigDirectory() + "/tor/");
|
||||||
|
RsTor::setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run
|
||||||
|
|
||||||
|
RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir)) ;
|
||||||
|
|
||||||
|
if(! RsTor::start() || RsTor::hasError())
|
||||||
|
{
|
||||||
|
std::cerr << "(EE) Tor cannot be started on your system: "+RsTor::errorMessage() << std::endl ;
|
||||||
|
return false ;
|
||||||
|
}
|
||||||
|
std::cerr << "(II) Tor has been started." << std::endl;
|
||||||
|
|
||||||
|
// now start/create the hidden service as needed.
|
||||||
|
|
||||||
|
std::string service_id;
|
||||||
|
RsTor::setupHiddenService();
|
||||||
|
|
||||||
|
while(RsTor::torStatus() != RsTorStatus::READY && RsTor::getHiddenServiceStatus(service_id) != RsTorHiddenServiceStatus::ONLINE) // runs until some status is reached: either tor works, or it fails.
|
||||||
|
{
|
||||||
|
rstime::rs_usleep(0.5*1000*1000) ;
|
||||||
|
|
||||||
|
std::cerr << "(II) Hidden service ID: " << service_id << ", status: " << (int)RsTor::getHiddenServiceStatus(service_id) << std::endl;
|
||||||
|
if(RsTor::hasError())
|
||||||
|
{
|
||||||
|
std::string error_msg = RsTor::errorMessage();
|
||||||
|
|
||||||
|
std::cerr << "(EE) Tor hidden service cannot be started: " << error_msg << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// process Qt event loop to deal with messages of online/offline info
|
||||||
|
// QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
RsInit::LoadCertificateStatus RsLoginHelper::attemptLogin(const RsPeerId& account, const std::string& password)
|
RsInit::LoadCertificateStatus RsLoginHelper::attemptLogin(const RsPeerId& account, const std::string& password)
|
||||||
{
|
{
|
||||||
if(isLoggedIn()) return RsInit::ERR_ALREADY_RUNNING;
|
if(isLoggedIn()) return RsInit::ERR_ALREADY_RUNNING;
|
||||||
@ -1950,6 +1999,16 @@ RsInit::LoadCertificateStatus RsLoginHelper::attemptLogin(const RsPeerId& accoun
|
|||||||
rsNotify->setDisableAskPassword(false) ;
|
rsNotify->setDisableAskPassword(false) ;
|
||||||
rsNotify->clearPgpPassphrase() ;
|
rsNotify->clearPgpPassphrase() ;
|
||||||
|
|
||||||
|
bool is_hidden_node = false;
|
||||||
|
bool is_auto_tor = false ;
|
||||||
|
bool is_first_time = false ;
|
||||||
|
|
||||||
|
RsAccounts::getCurrentAccountOptions(is_hidden_node,is_auto_tor,is_first_time);
|
||||||
|
|
||||||
|
if(is_auto_tor)
|
||||||
|
if(!RsInit::startAutoTor())
|
||||||
|
return RsInit::ERR_CANNOT_CONFIGURE_TOR;
|
||||||
|
|
||||||
if(ret == RsInit::OK && RsControl::instance()->StartupRetroShare() == 1)
|
if(ret == RsInit::OK && RsControl::instance()->StartupRetroShare() == 1)
|
||||||
return RsInit::OK;
|
return RsInit::OK;
|
||||||
|
|
||||||
|
@ -38,19 +38,19 @@
|
|||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
AddOnionCommand::AddOnionCommand(HiddenService *service)
|
AddOnionCommand::AddOnionCommand(HiddenService *service)
|
||||||
: m_service(service)
|
: m_service(service), mSucceeded([](){}), mFailed([](int){})
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_service);
|
assert(m_service);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddOnionCommand::isSuccessful() const
|
bool AddOnionCommand::isSuccessful() const
|
||||||
{
|
{
|
||||||
return statusCode() == 250 && m_errorMessage.isEmpty();
|
return statusCode() == 250 && m_errorMessage.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray AddOnionCommand::build()
|
ByteArray AddOnionCommand::build()
|
||||||
{
|
{
|
||||||
QByteArray out("ADD_ONION");
|
ByteArray out("ADD_ONION");
|
||||||
|
|
||||||
if (m_service->privateKey().isLoaded()) {
|
if (m_service->privateKey().isLoaded()) {
|
||||||
out += " ";
|
out += " ";
|
||||||
@ -61,42 +61,44 @@ QByteArray AddOnionCommand::build()
|
|||||||
out += " NEW:BEST"; // this is v3, but without control of key type. Generates a RSA1024 key on older Tor versions.
|
out += " NEW:BEST"; // this is v3, but without control of key type. Generates a RSA1024 key on older Tor versions.
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const HiddenService::Target &target, m_service->targets()) {
|
for(const HiddenService::Target& target: m_service->targets())
|
||||||
|
{
|
||||||
out += " Port=";
|
out += " Port=";
|
||||||
out += QByteArray::number(target.servicePort);
|
out += RsUtil::NumberToString(target.servicePort);
|
||||||
out += ",";
|
out += ",";
|
||||||
out += target.targetAddress.toString().toLatin1();
|
out += target.targetAddress;
|
||||||
out += ":";
|
out += ":";
|
||||||
out += QByteArray::number(target.targetPort);
|
out += RsUtil::NumberToString(target.targetPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append("\r\n");
|
out.append("\r\n");
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddOnionCommand::onReply(int statusCode, const QByteArray &data)
|
void AddOnionCommand::onReply(int statusCode, const ByteArray &data)
|
||||||
{
|
{
|
||||||
TorControlCommand::onReply(statusCode, data);
|
TorControlCommand::onReply(statusCode, data);
|
||||||
if (statusCode != 250) {
|
if (statusCode != 250) {
|
||||||
m_errorMessage = QString::fromLatin1(data);
|
m_errorMessage = data.toString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray keyPrefix("PrivateKey=");
|
const ByteArray keyPrefix("PrivateKey=");
|
||||||
const QByteArray sidPrefix("ServiceID=");
|
const ByteArray sidPrefix("ServiceID=");
|
||||||
|
|
||||||
if(data.startsWith("ServiceID=")){
|
if(data.startsWith(sidPrefix))
|
||||||
QByteArray service_id = data.mid(sidPrefix.size());
|
{
|
||||||
|
ByteArray service_id = data.mid(sidPrefix.size());
|
||||||
m_service->setServiceId(service_id);
|
m_service->setServiceId(service_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.startsWith(keyPrefix)) {
|
if (data.startsWith(keyPrefix))
|
||||||
|
{
|
||||||
QByteArray keyData(data.mid(keyPrefix.size()));
|
ByteArray keyData(data.mid(keyPrefix.size()));
|
||||||
CryptoKey key;
|
CryptoKey key;
|
||||||
|
|
||||||
if (!key.loadFromTorMessage(keyData)) {
|
if (!key.loadFromTorMessage(keyData)) {
|
||||||
m_errorMessage = QStringLiteral("Key structure check failed");
|
m_errorMessage = "Key structure check failed";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,9 +110,9 @@ void AddOnionCommand::onFinished(int statusCode)
|
|||||||
{
|
{
|
||||||
TorControlCommand::onFinished(statusCode);
|
TorControlCommand::onFinished(statusCode);
|
||||||
if (isSuccessful())
|
if (isSuccessful())
|
||||||
emit succeeded();
|
mSucceeded();
|
||||||
else
|
else
|
||||||
emit failed(statusCode);
|
mFailed(statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,13 +30,9 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ADDONIONCOMMAND_H
|
#pragma once
|
||||||
#define ADDONIONCOMMAND_H
|
|
||||||
|
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
#include <QList>
|
|
||||||
#include <QPair>
|
|
||||||
#include <QVariant>
|
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
@ -45,33 +41,26 @@ class HiddenService;
|
|||||||
|
|
||||||
class AddOnionCommand : public TorControlCommand
|
class AddOnionCommand : public TorControlCommand
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(AddOnionCommand)
|
|
||||||
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage CONSTANT)
|
|
||||||
Q_PROPERTY(bool successful READ isSuccessful CONSTANT)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AddOnionCommand(HiddenService *service);
|
AddOnionCommand(HiddenService *service);
|
||||||
|
|
||||||
QByteArray build();
|
ByteArray build();
|
||||||
|
|
||||||
QString errorMessage() const { return m_errorMessage; }
|
std::string errorMessage() const { return m_errorMessage; }
|
||||||
bool isSuccessful() const;
|
bool isSuccessful() const;
|
||||||
|
|
||||||
signals:
|
void set_succeeded_callback(const std::function<void(void)>& f) { mSucceeded=f;}
|
||||||
void succeeded();
|
void set_failed_callback(const std::function<void(int)>& f) { mFailed=f;}
|
||||||
void failed(int code);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
HiddenService *m_service;
|
HiddenService *m_service;
|
||||||
QString m_errorMessage;
|
std::string m_errorMessage;
|
||||||
|
|
||||||
virtual void onReply(int statusCode, const QByteArray &data);
|
std::function<void(void)> mSucceeded;
|
||||||
|
std::function<void(int)> mFailed;
|
||||||
|
|
||||||
|
virtual void onReply(int statusCode, const ByteArray &data);
|
||||||
virtual void onFinished(int statusCode);
|
virtual void onFinished(int statusCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ADDONIONCOMMAND_H
|
|
||||||
|
|
@ -38,27 +38,29 @@ AuthenticateCommand::AuthenticateCommand()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray AuthenticateCommand::build(const QByteArray &data)
|
ByteArray AuthenticateCommand::build(const ByteArray& data)
|
||||||
{
|
{
|
||||||
if (data.isNull())
|
if (data.isNull())
|
||||||
return QByteArray("AUTHENTICATE\r\n");
|
return ByteArray("AUTHENTICATE\r\n");
|
||||||
|
|
||||||
return QByteArray("AUTHENTICATE ") + data.toHex() + "\r\n";
|
return ByteArray("AUTHENTICATE ") + data.toHex() + "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticateCommand::onReply(int statusCode, const QByteArray &data)
|
void AuthenticateCommand::onReply(int statusCode, const ByteArray &data)
|
||||||
{
|
{
|
||||||
TorControlCommand::onReply(statusCode, data);
|
TorControlCommand::onReply(statusCode, data);
|
||||||
m_statusMessage = QString::fromLatin1(data);
|
m_statusMessage = data.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticateCommand::onFinished(int statusCode)
|
void AuthenticateCommand::onFinished(int statusCode)
|
||||||
{
|
{
|
||||||
if (statusCode == 515) {
|
if (statusCode == 515) {
|
||||||
m_statusMessage = QStringLiteral("Authentication failed - incorrect password");
|
m_statusMessage = "Authentication failed - incorrect password";
|
||||||
} else if (statusCode != 250) {
|
}
|
||||||
if (m_statusMessage.isEmpty())
|
else if (statusCode != 250)
|
||||||
m_statusMessage = QStringLiteral("Authentication failed (error %1").arg(statusCode);
|
{
|
||||||
|
if (m_statusMessage.empty())
|
||||||
|
m_statusMessage = "Authentication failed (error " + RsUtil::NumberToString(statusCode) + ")";
|
||||||
}
|
}
|
||||||
TorControlCommand::onFinished(statusCode);
|
TorControlCommand::onFinished(statusCode);
|
||||||
}
|
}
|
@ -30,9 +30,9 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef AUTHENTICATECOMMAND_H
|
#pragma once
|
||||||
#define AUTHENTICATECOMMAND_H
|
|
||||||
|
|
||||||
|
#include "bytearray.h"
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
@ -40,24 +40,20 @@ namespace Tor
|
|||||||
|
|
||||||
class AuthenticateCommand : public TorControlCommand
|
class AuthenticateCommand : public TorControlCommand
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AuthenticateCommand();
|
AuthenticateCommand();
|
||||||
|
|
||||||
QByteArray build(const QByteArray &data = QByteArray());
|
ByteArray build(const ByteArray& data = ByteArray());
|
||||||
|
|
||||||
bool isSuccessful() const { return statusCode() == 250; }
|
bool isSuccessful() const { return statusCode() == 250; }
|
||||||
QString errorMessage() const { return m_statusMessage; }
|
std::string errorMessage() const { return m_statusMessage; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onReply(int statusCode, const QByteArray &data);
|
virtual void onReply(int statusCode, const ByteArray &data);
|
||||||
virtual void onFinished(int statusCode);
|
virtual void onFinished(int statusCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_statusMessage;
|
std::string m_statusMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // AUTHENTICATECOMMAND_H
|
|
153
libretroshare/src/tor/CryptoKey.cpp
Normal file
153
libretroshare/src/tor/CryptoKey.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the names of the copyright owners nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "util/rsdebug.h"
|
||||||
|
#include "util/rsrandom.h"
|
||||||
|
#include "util/rsdir.h"
|
||||||
|
#include "retroshare/rsids.h"
|
||||||
|
#include "bytearray.h"
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
||||||
|
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
|
||||||
|
{
|
||||||
|
*p = r->p;
|
||||||
|
*q = r->q;
|
||||||
|
}
|
||||||
|
#define RSA_bits(o) (BN_num_bits((o)->n))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CryptoKey::CryptoKey()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoKey::~CryptoKey()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CryptoKey::clear()
|
||||||
|
{
|
||||||
|
key_data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::loadFromFile(const std::string& path)
|
||||||
|
{
|
||||||
|
FILE *file = fopen(path.c_str(),"r");
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
RsWarn() << "Failed to open Tor key file " << path << ": errno = " << errno ;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray data ;
|
||||||
|
int c;
|
||||||
|
while(EOF != (c=fgetc(file)))
|
||||||
|
data.push_back((unsigned char)c);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
if(data.startsWith("-----BEGIN RSA PRIVATE KEY-----"))
|
||||||
|
{
|
||||||
|
std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl;
|
||||||
|
|
||||||
|
// This to be compliant with old format. New format is oblivious to the type of key so we dont need a header
|
||||||
|
data = data.replace(ByteArray("-----BEGIN RSA PRIVATE KEY-----"),ByteArray());
|
||||||
|
data = data.replace(ByteArray("-----END RSA PRIVATE KEY-----"),ByteArray());
|
||||||
|
data = data.replace(ByteArray("\n"),ByteArray());
|
||||||
|
data = data.replace(ByteArray("\t"),ByteArray());
|
||||||
|
|
||||||
|
data = ByteArray("RSA1024:")+data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Have read the following key: " << std::endl;
|
||||||
|
std::cerr << data.toString() << std::endl;
|
||||||
|
|
||||||
|
key_data = data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CryptoKey::loadFromTorMessage(const ByteArray& b)
|
||||||
|
{
|
||||||
|
// note: We should probably check the structure a bit more, for security.
|
||||||
|
|
||||||
|
std::cerr << "Loading new key:" << std::endl;
|
||||||
|
|
||||||
|
if(b.startsWith("RSA1024"))
|
||||||
|
std::cerr << " type: RSA-1024 (Tor v2)" << std::endl;
|
||||||
|
else if(b.startsWith("ED25519-V3"))
|
||||||
|
std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl;
|
||||||
|
else if(b.indexOf(':'))
|
||||||
|
{
|
||||||
|
std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toString() << "\". Not accepted." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_data = b;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */
|
||||||
|
ByteArray torControlHashedPassword(const ByteArray &password)
|
||||||
|
{
|
||||||
|
ByteArray salt(8);
|
||||||
|
RsRandom::random_bytes(&salt[0],8);
|
||||||
|
|
||||||
|
uint32_t count = ((uint32_t)16 + (96 & 15)) << ((96 >> 4) + 6);
|
||||||
|
|
||||||
|
SHA_CTX hash;
|
||||||
|
SHA1_Init(&hash);
|
||||||
|
|
||||||
|
ByteArray tmp = salt + password;
|
||||||
|
while (count)
|
||||||
|
{
|
||||||
|
int c = std::min((size_t)count, tmp.size());
|
||||||
|
SHA1_Update(&hash, reinterpret_cast<const void*>(tmp.data()), c);
|
||||||
|
count -= c;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char md[20];
|
||||||
|
SHA1_Final(md, &hash);
|
||||||
|
|
||||||
|
/* 60 is the hex-encoded value of 96, which is a constant used by Tor's algorithm. */
|
||||||
|
return ByteArray("16:") + salt.toHex().toUpper() + ByteArray("60") + ByteArray(md, 20).toHex().toUpper();
|
||||||
|
}
|
@ -30,22 +30,35 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SECURERNG_H
|
#pragma once
|
||||||
#define SECURERNG_H
|
|
||||||
|
|
||||||
#include <QByteArray>
|
#include "bytearray.h"
|
||||||
|
|
||||||
class SecureRNG
|
class CryptoKey
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool seed();
|
enum KeyType {
|
||||||
|
PrivateKey,
|
||||||
|
PublicKey
|
||||||
|
};
|
||||||
|
|
||||||
static void random(char *buf, int size);
|
enum KeyFormat {
|
||||||
static QByteArray random(int size);
|
PEM,
|
||||||
|
DER
|
||||||
|
};
|
||||||
|
|
||||||
static QByteArray randomPrintable(int length);
|
CryptoKey();
|
||||||
static unsigned randomInt(unsigned max);
|
~CryptoKey();
|
||||||
static quint64 randomInt64(quint64 max);
|
|
||||||
|
bool loadFromFile(const std::string &path);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
const ByteArray bytes() const { return key_data; }
|
||||||
|
bool loadFromTorMessage(const ByteArray& b);
|
||||||
|
bool isLoaded() const { return !key_data.isNull(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ByteArray key_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SECURERNG_H
|
ByteArray torControlHashedPassword(const ByteArray &password);
|
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
#include "GetConfCommand.h"
|
#include "GetConfCommand.h"
|
||||||
#include "StrUtil.h"
|
#include "StrUtil.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
@ -41,25 +40,25 @@ GetConfCommand::GetConfCommand(Type t)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray GetConfCommand::build(const QByteArray &key)
|
ByteArray GetConfCommand::build(const std::string &key)
|
||||||
{
|
{
|
||||||
return build(QList<QByteArray>() << key);
|
return build(std::list<std::string> { key } );
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray GetConfCommand::build(const QList<QByteArray> &keys)
|
ByteArray GetConfCommand::build(const std::list<std::string> &keys)
|
||||||
{
|
{
|
||||||
QByteArray out;
|
ByteArray out;
|
||||||
if (type == GetConf) {
|
if (type == GetConf) {
|
||||||
out = "GETCONF";
|
out = "GETCONF";
|
||||||
} else if (type == GetInfo) {
|
} else if (type == GetInfo) {
|
||||||
out = "GETINFO";
|
out = "GETINFO";
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(false);
|
assert(false);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const QByteArray &key, keys) {
|
for(const ByteArray &key: keys) {
|
||||||
out.append(' ');
|
out.push_back(' ');
|
||||||
out.append(key);
|
out.append(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,49 +66,29 @@ QByteArray GetConfCommand::build(const QList<QByteArray> &keys)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetConfCommand::onReply(int statusCode, const QByteArray &data)
|
void GetConfCommand::onReply(int statusCode, const ByteArray &data)
|
||||||
{
|
{
|
||||||
TorControlCommand::onReply(statusCode, data);
|
TorControlCommand::onReply(statusCode, data);
|
||||||
if (statusCode != 250)
|
if (statusCode != 250)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int kep = data.indexOf('=');
|
int kep = data.indexOf('=');
|
||||||
QString key = QString::fromLatin1(data.mid(0, kep));
|
std::string key = data.mid(0, kep).toString();
|
||||||
QVariant value;
|
std::string value;
|
||||||
if (kep >= 0)
|
if (kep >= 0)
|
||||||
value = QString::fromLatin1(unquotedString(data.mid(kep + 1)));
|
value = unquotedString(data.mid(kep + 1)).toString();
|
||||||
|
|
||||||
m_lastKey = key;
|
m_lastKey = key;
|
||||||
QVariantMap::iterator it = m_results.find(key);
|
m_results[key].push_back(value);
|
||||||
if (it != m_results.end()) {
|
|
||||||
// Make a list of values
|
|
||||||
QVariantList results = it->toList();
|
|
||||||
if (results.isEmpty())
|
|
||||||
results.append(*it);
|
|
||||||
results.append(value);
|
|
||||||
*it = QVariant(results);
|
|
||||||
} else {
|
|
||||||
m_results.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetConfCommand::onDataLine(const QByteArray &data)
|
void GetConfCommand::onDataLine(const ByteArray &data)
|
||||||
{
|
{
|
||||||
if (m_lastKey.isEmpty()) {
|
if (m_lastKey.empty()) {
|
||||||
qWarning() << "torctrl: Unexpected data line in GetConf command";
|
RsWarn() << "torctrl: Unexpected data line in GetConf command";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_results[m_lastKey].push_back(data.toString());
|
||||||
QVariantMap::iterator it = m_results.find(m_lastKey);
|
|
||||||
if (it != m_results.end()) {
|
|
||||||
QVariantList results = it->toList();
|
|
||||||
if (results.isEmpty() && !it->toByteArray().isEmpty())
|
|
||||||
results.append(*it);
|
|
||||||
results.append(data);
|
|
||||||
*it = QVariant(results);
|
|
||||||
} else {
|
|
||||||
m_results.insert(m_lastKey, QVariantList() << data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetConfCommand::onDataFinished()
|
void GetConfCommand::onDataFinished()
|
||||||
@ -117,8 +96,13 @@ void GetConfCommand::onDataFinished()
|
|||||||
m_lastKey.clear();
|
m_lastKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant GetConfCommand::get(const QByteArray &key) const
|
std::list<std::string> GetConfCommand::get(const std::string& key) const
|
||||||
{
|
{
|
||||||
return m_results.value(QString::fromLatin1(key));
|
auto it = m_results.find(key);
|
||||||
|
|
||||||
|
if(it != m_results.end())
|
||||||
|
return it->second;
|
||||||
|
else
|
||||||
|
return std::list<std::string>();
|
||||||
}
|
}
|
||||||
|
|
@ -30,23 +30,16 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GETCONFCOMMAND_H
|
#pragma once
|
||||||
#define GETCONFCOMMAND_H
|
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
#include <QList>
|
|
||||||
#include <QVariantMap>
|
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
|
|
||||||
class GetConfCommand : public TorControlCommand
|
class GetConfCommand : public TorControlCommand
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(GetConfCommand)
|
|
||||||
|
|
||||||
Q_PROPERTY(QVariantMap results READ results CONSTANT)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Type {
|
enum Type {
|
||||||
GetConf,
|
GetConf,
|
||||||
@ -56,22 +49,20 @@ public:
|
|||||||
|
|
||||||
GetConfCommand(Type type);
|
GetConfCommand(Type type);
|
||||||
|
|
||||||
QByteArray build(const QByteArray &key);
|
ByteArray build(const std::string &key);
|
||||||
QByteArray build(const QList<QByteArray> &keys);
|
ByteArray build(const std::list<std::string> &keys);
|
||||||
|
|
||||||
const QVariantMap &results() const { return m_results; }
|
const std::map<std::string,std::list<std::string> > &results() const { return m_results; }
|
||||||
QVariant get(const QByteArray &key) const;
|
std::list<std::string> get(const std::string &key) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onReply(int statusCode, const QByteArray &data);
|
virtual void onReply(int statusCode, const ByteArray &data);
|
||||||
virtual void onDataLine(const QByteArray &data);
|
virtual void onDataLine(const ByteArray &data);
|
||||||
virtual void onDataFinished();
|
virtual void onDataFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariantMap m_results;
|
std::map<std::string,std::list<std::string> > m_results;
|
||||||
QString m_lastKey;
|
std::string m_lastKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // GETCONFCOMMAND_H
|
|
@ -32,34 +32,32 @@
|
|||||||
|
|
||||||
#include "HiddenService.h"
|
#include "HiddenService.h"
|
||||||
#include "TorControl.h"
|
#include "TorControl.h"
|
||||||
#include "TorSocket.h"
|
|
||||||
#include "CryptoKey.h"
|
#include "CryptoKey.h"
|
||||||
#include "Useful.h"
|
#include "util/rsdir.h"
|
||||||
#include <QDir>
|
|
||||||
#include <QFile>
|
#include <fstream>
|
||||||
#include <QTimer>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
HiddenService::HiddenService(QObject *parent)
|
HiddenService::HiddenService(HiddenServiceClient *client)
|
||||||
: QObject(parent), m_status(NotCreated)
|
: m_status(NotCreated), m_client(client)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
HiddenService::HiddenService(const QString &path, QObject *parent)
|
HiddenService::HiddenService(HiddenServiceClient *client,const std::string& path)
|
||||||
: QObject(parent), m_dataPath(path), m_status(NotCreated)
|
: m_dataPath(path), m_status(NotCreated), m_client(client)
|
||||||
{
|
{
|
||||||
/* Set the initial status and, if possible, load the hostname */
|
/* Set the initial status and, if possible, load the hostname */
|
||||||
if (QDir(m_dataPath).exists(QLatin1String("private_key"))) {
|
if(RsDirUtil::fileExists(m_dataPath + "/private_key"))
|
||||||
|
{
|
||||||
loadPrivateKey();
|
loadPrivateKey();
|
||||||
if (!m_hostname.isEmpty())
|
if (!m_hostname.empty())
|
||||||
m_status = Offline;
|
m_status = Offline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HiddenService::HiddenService(const CryptoKey &privateKey, const QString &path, QObject *parent)
|
HiddenService::HiddenService(HiddenServiceClient *client,const CryptoKey &privateKey, const std::string &path)
|
||||||
: QObject(parent), m_dataPath(path), m_status(NotCreated)
|
: m_dataPath(path), m_status(NotCreated), m_client(client)
|
||||||
{
|
{
|
||||||
setPrivateKey(privateKey);
|
setPrivateKey(privateKey);
|
||||||
m_status = Offline;
|
m_status = Offline;
|
||||||
@ -73,69 +71,89 @@ void HiddenService::setStatus(Status newStatus)
|
|||||||
Status old = m_status;
|
Status old = m_status;
|
||||||
m_status = newStatus;
|
m_status = newStatus;
|
||||||
|
|
||||||
emit statusChanged(m_status, old);
|
if(m_client)
|
||||||
|
m_client->hiddenServiceStatusChanged(m_status,old); //emit statusChanged(m_status, old);
|
||||||
|
|
||||||
if (m_status == Online)
|
if (m_status == Online)
|
||||||
emit serviceOnline();
|
if(m_client)
|
||||||
|
m_client->hiddenServiceOnline(); //emit serviceOnline();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HiddenService::addTarget(const Target &target)
|
void HiddenService::addTarget(const Target &target)
|
||||||
{
|
{
|
||||||
m_targets.append(target);
|
m_targets.push_back(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HiddenService::addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort)
|
void HiddenService::addTarget(uint16_t servicePort, std::string targetAddress, uint16_t targetPort)
|
||||||
{
|
{
|
||||||
Target t = { targetAddress, servicePort, targetPort };
|
Target t = { targetAddress, servicePort, targetPort };
|
||||||
m_targets.append(t);
|
m_targets.push_back(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HiddenService::setServiceId(const QByteArray& sid)
|
void HiddenService::setServiceId(const ByteArray& sid)
|
||||||
{
|
{
|
||||||
m_service_id = sid;
|
m_service_id = sid;
|
||||||
m_hostname = sid + ".onion";
|
m_hostname = sid.toString() + ".onion";
|
||||||
|
|
||||||
emit hostnameChanged();
|
if(m_client)
|
||||||
|
m_client->hiddenServiceHostnameChanged(); // emit hostnameChanged();
|
||||||
}
|
}
|
||||||
void HiddenService::setPrivateKey(const CryptoKey &key)
|
void HiddenService::setPrivateKey(const CryptoKey &key)
|
||||||
{
|
{
|
||||||
if (m_privateKey.isLoaded()) {
|
if (m_privateKey.isLoaded()) {
|
||||||
BUG() << "Cannot change the private key on an existing HiddenService";
|
RsErr() << "Cannot change the private key on an existing HiddenService";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
if (!key.isPrivate()) {
|
|
||||||
BUG() << "Cannot create a hidden service with a public key";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_privateKey = key;
|
m_privateKey = key;
|
||||||
|
|
||||||
emit privateKeyChanged();
|
if(m_client)
|
||||||
|
m_client->hiddenServicePrivateKeyChanged(); //emit privateKeyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HiddenService::loadPrivateKey()
|
bool HiddenService::loadPrivateKey()
|
||||||
{
|
{
|
||||||
if (m_privateKey.isLoaded() || m_dataPath.isEmpty())
|
if (m_privateKey.isLoaded() || m_dataPath.empty())
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"));
|
bool ok = m_privateKey.loadFromFile(m_dataPath + "/private_key");
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qWarning() << "Failed to load hidden service key";
|
RsWarn() << "Failed to load hidden service key";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit privateKeyChanged();
|
// Also load the onion address stored in "hostname" file. This is not needed, except for early display
|
||||||
|
// of the onion address, since the onion address will be re-computed by Tor (to the same value) when the
|
||||||
|
// service is published.
|
||||||
|
|
||||||
|
std::ifstream i((m_dataPath + "/hostname").c_str());
|
||||||
|
|
||||||
|
if(i)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
i >> s;
|
||||||
|
if(ByteArray(s).endsWith(ByteArray(".onion")))
|
||||||
|
{
|
||||||
|
m_hostname = s;
|
||||||
|
m_service_id = s.substr(0,s.length() - std::string(".onion").length());
|
||||||
|
|
||||||
|
RsDbg() << "Read existing hostname: " << m_hostname;
|
||||||
|
}
|
||||||
|
i.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_client)
|
||||||
|
m_client->hiddenServicePrivateKeyChanged(); // emit privateKeyChanged();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HiddenService::servicePublished()
|
void HiddenService::servicePublished()
|
||||||
{
|
{
|
||||||
loadPrivateKey();
|
loadPrivateKey();
|
||||||
|
|
||||||
if (m_hostname.isEmpty()) {
|
if (m_hostname.empty()) {
|
||||||
std::cerr << "Failed to read hidden service hostname" << std::endl;
|
std::cerr << "Failed to read hidden service hostname" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
@ -30,31 +30,35 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef HIDDENSERVICE_H
|
#pragma once
|
||||||
#define HIDDENSERVICE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QList>
|
|
||||||
#include "CryptoKey.h"
|
#include "CryptoKey.h"
|
||||||
|
#include "bytearray.h"
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
|
|
||||||
class TorSocket;
|
// This class is used to receive synchroneous notifications from the hidden service.
|
||||||
|
// Each client should implement its own notification handling.
|
||||||
|
|
||||||
class HiddenService : public QObject
|
class HiddenServiceClient
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
public:
|
||||||
Q_DISABLE_COPY(HiddenService)
|
virtual void hiddenServiceStatusChanged(int /* newStatus */, int /* oldStatus */) =0;
|
||||||
|
virtual void hiddenServiceOnline() =0;
|
||||||
|
virtual void hiddenServicePrivateKeyChanged() =0;
|
||||||
|
virtual void hiddenServiceHostnameChanged() =0;
|
||||||
|
};
|
||||||
|
|
||||||
friend class TorControlPrivate;
|
class HiddenService
|
||||||
|
{
|
||||||
|
friend class TorControl;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Target
|
struct Target
|
||||||
{
|
{
|
||||||
QHostAddress targetAddress;
|
std::string targetAddress;
|
||||||
quint16 servicePort, targetPort;
|
uint16_t servicePort, targetPort;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Status
|
enum Status
|
||||||
@ -64,45 +68,42 @@ public:
|
|||||||
Online /* Published */
|
Online /* Published */
|
||||||
};
|
};
|
||||||
|
|
||||||
HiddenService(QObject *parent = 0);
|
HiddenService(HiddenServiceClient *client);
|
||||||
HiddenService(const QString &dataPath, QObject *parent = 0);
|
HiddenService(HiddenServiceClient *client, const std::string &dataPath);
|
||||||
HiddenService(const CryptoKey &privateKey, const QString &dataPath = QString(), QObject *parent = 0);
|
HiddenService(HiddenServiceClient *client, const CryptoKey &privateKey, const std::string &dataPath = std::string());
|
||||||
|
|
||||||
Status status() const { return m_status; }
|
Status status() const { return m_status; }
|
||||||
|
|
||||||
const QString& hostname() const { return m_hostname; }
|
const std::string& hostname() const { return m_hostname; }
|
||||||
const QString serviceId() const { return QString(m_service_id); }
|
const std::string serviceId() const { return m_service_id.toString(); }
|
||||||
const QString& dataPath() const { return m_dataPath; }
|
const std::string& dataPath() const { return m_dataPath; }
|
||||||
|
|
||||||
CryptoKey privateKey() { return m_privateKey; }
|
CryptoKey privateKey() { return m_privateKey; }
|
||||||
void setPrivateKey(const CryptoKey &privateKey);
|
void setPrivateKey(const CryptoKey &privateKey);
|
||||||
void setServiceId(const QByteArray& sid);
|
void setServiceId(const ByteArray &sid);
|
||||||
|
|
||||||
const QList<Target> &targets() const { return m_targets; }
|
const std::list<Target> &targets() const { return m_targets; }
|
||||||
void addTarget(const Target &target);
|
void addTarget(const Target &target);
|
||||||
void addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort);
|
void addTarget(uint16_t servicePort, std::string targetAddress, uint16_t targetPort);
|
||||||
|
|
||||||
signals:
|
|
||||||
void statusChanged(int newStatus, int oldStatus);
|
|
||||||
void serviceOnline();
|
|
||||||
void privateKeyChanged();
|
|
||||||
void hostnameChanged();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void servicePublished();
|
void servicePublished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_dataPath;
|
std::string m_dataPath;
|
||||||
QList<Target> m_targets;
|
std::list<Target> m_targets;
|
||||||
QString m_hostname;
|
std::string m_hostname;
|
||||||
Status m_status;
|
Status m_status;
|
||||||
CryptoKey m_privateKey;
|
CryptoKey m_privateKey;
|
||||||
QByteArray m_service_id;
|
ByteArray m_service_id;
|
||||||
|
|
||||||
void loadPrivateKey();
|
bool loadPrivateKey();
|
||||||
void setStatus(Status newStatus);
|
void setStatus(Status newStatus);
|
||||||
|
|
||||||
|
HiddenServiceClient *m_client;
|
||||||
|
|
||||||
|
// make the object non copyable
|
||||||
|
HiddenService(const HiddenService&) {}
|
||||||
|
HiddenService& operator=(const HiddenService&) { return *this ; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HIDDENSERVICE_H
|
|
@ -30,10 +30,11 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include "PendingOperation.h"
|
#include "PendingOperation.h"
|
||||||
|
|
||||||
PendingOperation::PendingOperation(QObject *parent)
|
PendingOperation::PendingOperation()
|
||||||
: QObject(parent), m_finished(false)
|
: m_finished(false),mFinishedCallback([](){}), mSuccessCallback([](){}),mErrorCallback([](const std::string&){})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,41 +45,44 @@ bool PendingOperation::isFinished() const
|
|||||||
|
|
||||||
bool PendingOperation::isSuccess() const
|
bool PendingOperation::isSuccess() const
|
||||||
{
|
{
|
||||||
return m_finished && m_errorMessage.isNull();
|
return m_finished && m_errorMessage.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PendingOperation::isError() const
|
bool PendingOperation::isError() const
|
||||||
{
|
{
|
||||||
return m_finished && !m_errorMessage.isNull();
|
return m_finished && !m_errorMessage.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PendingOperation::errorMessage() const
|
std::string PendingOperation::errorMessage() const
|
||||||
{
|
{
|
||||||
return m_errorMessage;
|
return m_errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PendingOperation::finishWithError(const QString &message)
|
void PendingOperation::finishWithError(const std::string &message)
|
||||||
{
|
{
|
||||||
if (message.isEmpty())
|
if (message.empty())
|
||||||
m_errorMessage = QStringLiteral("Unknown Error");
|
m_errorMessage = "Unknown Error";
|
||||||
m_errorMessage = message;
|
m_errorMessage = message;
|
||||||
|
|
||||||
if (!m_finished) {
|
if (!m_finished) {
|
||||||
m_finished = true;
|
m_finished = true;
|
||||||
emit finished();
|
|
||||||
emit error(m_errorMessage);
|
mErrorCallback(m_errorMessage);
|
||||||
|
mFinishedCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PendingOperation::finishWithSuccess()
|
void PendingOperation::finishWithSuccess()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_errorMessage.isNull());
|
assert(m_errorMessage.empty());
|
||||||
|
|
||||||
if (!m_finished) {
|
if (!m_finished) {
|
||||||
m_finished = true;
|
m_finished = true;
|
||||||
emit finished();
|
|
||||||
if (isSuccess())
|
if (isSuccess())
|
||||||
emit success();
|
mSuccessCallback();
|
||||||
|
|
||||||
|
mFinishedCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,10 +30,10 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PENDINGOPERATION_H
|
#pragma once
|
||||||
#define PENDINGOPERATION_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
/* Represents an asynchronous operation for reporting status
|
/* Represents an asynchronous operation for reporting status
|
||||||
*
|
*
|
||||||
@ -48,40 +48,27 @@
|
|||||||
* PendingOperation will emit finished() and one of success() or
|
* PendingOperation will emit finished() and one of success() or
|
||||||
* error() when completed.
|
* error() when completed.
|
||||||
*/
|
*/
|
||||||
class PendingOperation : public QObject
|
class PendingOperation
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(bool isFinished READ isFinished NOTIFY finished FINAL)
|
|
||||||
Q_PROPERTY(bool isSuccess READ isSuccess NOTIFY success FINAL)
|
|
||||||
Q_PROPERTY(bool isError READ isError NOTIFY error FINAL)
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY finished FINAL)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PendingOperation(QObject *parent = 0);
|
PendingOperation();
|
||||||
|
|
||||||
bool isFinished() const;
|
bool isFinished() const;
|
||||||
bool isSuccess() const;
|
bool isSuccess() const;
|
||||||
bool isError() const;
|
bool isError() const;
|
||||||
QString errorMessage() const;
|
std::string errorMessage() const;
|
||||||
|
|
||||||
signals:
|
void finishWithError(const std::string &errorMessage);
|
||||||
// Always emitted once when finished, regardless of status
|
|
||||||
void finished();
|
|
||||||
|
|
||||||
// One of error() or success() is emitted once
|
|
||||||
void error(const QString &errorMessage);
|
|
||||||
void success();
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void finishWithError(const QString &errorMessage);
|
|
||||||
void finishWithSuccess();
|
void finishWithSuccess();
|
||||||
|
|
||||||
|
void set_finished_callback(const std::function<void(void)>& f) { mFinishedCallback = f; }
|
||||||
private:
|
private:
|
||||||
bool m_finished;
|
bool m_finished;
|
||||||
QString m_errorMessage;
|
std::string m_errorMessage;
|
||||||
|
|
||||||
|
std::function<void(void)> mFinishedCallback;
|
||||||
|
std::function<void(void)> mSuccessCallback;
|
||||||
|
std::function<void(const std::string&)> mErrorCallback;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(PendingOperation*)
|
|
||||||
|
|
||||||
#endif
|
|
@ -33,21 +33,20 @@
|
|||||||
#include "ProtocolInfoCommand.h"
|
#include "ProtocolInfoCommand.h"
|
||||||
#include "TorControl.h"
|
#include "TorControl.h"
|
||||||
#include "StrUtil.h"
|
#include "StrUtil.h"
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
ProtocolInfoCommand::ProtocolInfoCommand(TorControl *m)
|
ProtocolInfoCommand::ProtocolInfoCommand(TorControl *m)
|
||||||
: manager(m)
|
: manager(m),m_authMethods(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ProtocolInfoCommand::build()
|
ByteArray ProtocolInfoCommand::build()
|
||||||
{
|
{
|
||||||
return QByteArray("PROTOCOLINFO 1\r\n");
|
return ByteArray("PROTOCOLINFO 1\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data)
|
void ProtocolInfoCommand::onReply(int statusCode, const ByteArray &data)
|
||||||
{
|
{
|
||||||
TorControlCommand::onReply(statusCode, data);
|
TorControlCommand::onReply(statusCode, data);
|
||||||
if (statusCode != 250)
|
if (statusCode != 250)
|
||||||
@ -55,14 +54,15 @@ void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data)
|
|||||||
|
|
||||||
if (data.startsWith("AUTH "))
|
if (data.startsWith("AUTH "))
|
||||||
{
|
{
|
||||||
QList<QByteArray> tokens = splitQuotedStrings(data.mid(5), ' ');
|
std::list<ByteArray> tokens = splitQuotedStrings(data.mid(5), ' ');
|
||||||
|
|
||||||
foreach (QByteArray token, tokens)
|
for(ByteArray token: tokens)
|
||||||
{
|
{
|
||||||
if (token.startsWith("METHODS="))
|
if (token.startsWith("METHODS="))
|
||||||
{
|
{
|
||||||
QList<QByteArray> textMethods = unquotedString(token.mid(8)).split(',');
|
std::list<ByteArray> textMethods = unquotedString(token.mid(8)).split(',');
|
||||||
for (QList<QByteArray>::Iterator it = textMethods.begin(); it != textMethods.end(); ++it)
|
|
||||||
|
for (std::list<ByteArray>::iterator it = textMethods.begin(); it != textMethods.end(); ++it)
|
||||||
{
|
{
|
||||||
if (*it == "NULL")
|
if (*it == "NULL")
|
||||||
m_authMethods |= AuthNull;
|
m_authMethods |= AuthNull;
|
||||||
@ -74,12 +74,12 @@ void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data)
|
|||||||
}
|
}
|
||||||
else if (token.startsWith("COOKIEFILE="))
|
else if (token.startsWith("COOKIEFILE="))
|
||||||
{
|
{
|
||||||
m_cookieFile = QString::fromLatin1(unquotedString(token.mid(11)));
|
m_cookieFile = unquotedString(token.mid(11)).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (data.startsWith("VERSION Tor="))
|
else if (data.startsWith("VERSION Tor="))
|
||||||
{
|
{
|
||||||
m_torVersion = QString::fromLatin1(unquotedString(data.mid(12, data.indexOf(' ', 12))));
|
m_torVersion = unquotedString(data.mid(12, data.indexOf(' ', 12))).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,11 +30,10 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PROTOCOLINFOCOMMAND_H
|
#pragma once
|
||||||
#define PROTOCOLINFOCOMMAND_H
|
|
||||||
|
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
#include <QFlags>
|
#include "retroshare/rsflags.h"
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
@ -43,36 +42,31 @@ class TorControl;
|
|||||||
|
|
||||||
class ProtocolInfoCommand : public TorControlCommand
|
class ProtocolInfoCommand : public TorControlCommand
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(ProtocolInfoCommand)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum AuthMethod
|
enum
|
||||||
{
|
{
|
||||||
AuthUnknown = 0,
|
AuthUnknown = 0x0,
|
||||||
AuthNull = 0x1,
|
AuthNull = 0x1,
|
||||||
AuthHashedPassword = 0x2,
|
AuthHashedPassword = 0x2,
|
||||||
AuthCookie = 0x4
|
AuthCookie = 0x4
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(AuthMethods, AuthMethod)
|
typedef uint8_t AuthMethod;
|
||||||
|
|
||||||
ProtocolInfoCommand(TorControl *manager);
|
ProtocolInfoCommand(TorControl *manager);
|
||||||
QByteArray build();
|
ByteArray build();
|
||||||
|
|
||||||
AuthMethods authMethods() const { return m_authMethods; }
|
AuthMethod authMethods() const { return m_authMethods; }
|
||||||
QString torVersion() const { return m_torVersion; }
|
std::string torVersion() const { return m_torVersion; }
|
||||||
QString cookieFile() const { return m_cookieFile; }
|
std::string cookieFile() const { return m_cookieFile; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onReply(int statusCode, const QByteArray &data);
|
virtual void onReply(int statusCode, const ByteArray &data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TorControl *manager;
|
TorControl *manager;
|
||||||
AuthMethods m_authMethods;
|
AuthMethod m_authMethods;
|
||||||
QString m_torVersion;
|
std::string m_torVersion;
|
||||||
QString m_cookieFile;
|
std::string m_cookieFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PROTOCOLINFOCOMMAND_H
|
|
@ -36,7 +36,7 @@
|
|||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
SetConfCommand::SetConfCommand()
|
SetConfCommand::SetConfCommand()
|
||||||
: m_resetMode(false)
|
: m_resetMode(false), mConfSucceeded([](){}), mConfFailed([](int){})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,57 +50,44 @@ bool SetConfCommand::isSuccessful() const
|
|||||||
return statusCode() == 250;
|
return statusCode() == 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SetConfCommand::build(const QByteArray &key, const QByteArray &value)
|
ByteArray SetConfCommand::build(const std::string &key, const std::string &value)
|
||||||
{
|
{
|
||||||
return build(QList<QPair<QByteArray, QByteArray> >() << qMakePair(key, value));
|
return build(std::list<std::pair<std::string, std::string> > { std::make_pair(key, value) } );
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray SetConfCommand::build(const QVariantMap &data)
|
ByteArray SetConfCommand::build(const std::list<std::pair<std::string, std::string> >& data)
|
||||||
{
|
{
|
||||||
QList<QPair<QByteArray, QByteArray> > out;
|
ByteArray out(m_resetMode ? "RESETCONF" : "SETCONF");
|
||||||
|
|
||||||
for (QVariantMap::ConstIterator it = data.begin(); it != data.end(); it++) {
|
for (auto& p:data)
|
||||||
QByteArray key = it.key().toLatin1();
|
{
|
||||||
|
out += " " ;
|
||||||
|
out += p.first;
|
||||||
|
|
||||||
if (static_cast<QMetaType::Type>(it.value().type()) == QMetaType::QVariantList) {
|
if (!p.second.empty())
|
||||||
QVariantList values = it.value().value<QVariantList>();
|
{
|
||||||
foreach (const QVariant &value, values)
|
out += "=" ;
|
||||||
out.append(qMakePair(key, value.toString().toLatin1()));
|
out += quotedString(p.second);
|
||||||
} else {
|
|
||||||
out.append(qMakePair(key, it.value().toString().toLatin1()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return build(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SetConfCommand::build(const QList<QPair<QByteArray, QByteArray> > &data)
|
|
||||||
{
|
|
||||||
QByteArray out(m_resetMode ? "RESETCONF" : "SETCONF");
|
|
||||||
|
|
||||||
for (int i = 0; i < data.size(); i++) {
|
|
||||||
out += " " + data[i].first;
|
|
||||||
if (!data[i].second.isEmpty())
|
|
||||||
out += "=" + quotedString(data[i].second);
|
|
||||||
}
|
|
||||||
|
|
||||||
out.append("\r\n");
|
out.append("\r\n");
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetConfCommand::onReply(int statusCode, const QByteArray &data)
|
void SetConfCommand::onReply(int statusCode, const ByteArray &data)
|
||||||
{
|
{
|
||||||
TorControlCommand::onReply(statusCode, data);
|
TorControlCommand::onReply(statusCode, data);
|
||||||
if (statusCode != 250)
|
if (statusCode != 250)
|
||||||
m_errorMessage = QString::fromLatin1(data);
|
m_errorMessage = data.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetConfCommand::onFinished(int statusCode)
|
void SetConfCommand::onFinished(int statusCode)
|
||||||
{
|
{
|
||||||
TorControlCommand::onFinished(statusCode);
|
TorControlCommand::onFinished(statusCode);
|
||||||
if (isSuccessful())
|
if (isSuccessful())
|
||||||
emit setConfSucceeded();
|
mConfSucceeded();
|
||||||
else
|
else
|
||||||
emit setConfFailed(statusCode);
|
mConfFailed(statusCode);
|
||||||
}
|
}
|
||||||
|
|
@ -30,49 +30,39 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SETCONFCOMMAND_H
|
#pragma once
|
||||||
#define SETCONFCOMMAND_H
|
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
#include <QList>
|
|
||||||
#include <QPair>
|
|
||||||
#include <QVariant>
|
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
|
|
||||||
class SetConfCommand : public TorControlCommand
|
class SetConfCommand : public TorControlCommand
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(SetConfCommand)
|
|
||||||
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage CONSTANT)
|
|
||||||
Q_PROPERTY(bool successful READ isSuccessful CONSTANT)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SetConfCommand();
|
SetConfCommand();
|
||||||
|
|
||||||
void setResetMode(bool resetMode);
|
void setResetMode(bool resetMode);
|
||||||
|
|
||||||
QByteArray build(const QByteArray &key, const QByteArray &value);
|
ByteArray build(const std::string &key, const std::string &value);
|
||||||
QByteArray build(const QVariantMap &data);
|
ByteArray build(const std::list<std::pair<std::string, std::string> > &data);
|
||||||
QByteArray build(const QList<QPair<QByteArray, QByteArray> > &data);
|
|
||||||
|
|
||||||
QString errorMessage() const { return m_errorMessage; }
|
std::string errorMessage() const { return m_errorMessage; }
|
||||||
bool isSuccessful() const;
|
bool isSuccessful() const;
|
||||||
|
|
||||||
signals:
|
void set_ConfSucceeded_callback(const std::function<void(void)>& f) { mConfSucceeded=f; }
|
||||||
void setConfSucceeded();
|
void set_ConfFailed_callback (const std::function<void(int code)>& f){ mConfFailed=f; }
|
||||||
void setConfFailed(int code);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_errorMessage;
|
std::string m_errorMessage;
|
||||||
bool m_resetMode;
|
bool m_resetMode;
|
||||||
|
|
||||||
virtual void onReply(int statusCode, const QByteArray &data);
|
std::function<void(void)> mConfSucceeded;
|
||||||
|
std::function<void(int code)> mConfFailed;
|
||||||
|
|
||||||
|
virtual void onReply(int statusCode, const ByteArray &data);
|
||||||
virtual void onFinished(int statusCode);
|
virtual void onFinished(int statusCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SETCONFCOMMAND_H
|
|
@ -32,14 +32,14 @@
|
|||||||
|
|
||||||
#include "StrUtil.h"
|
#include "StrUtil.h"
|
||||||
|
|
||||||
QByteArray quotedString(const QByteArray &string)
|
ByteArray quotedString(const ByteArray &string)
|
||||||
{
|
{
|
||||||
QByteArray out;
|
ByteArray out;
|
||||||
out.reserve(string.size() * 2);
|
out.reserve(string.size() * 2);
|
||||||
|
|
||||||
out.append('"');
|
out.push_back('"');
|
||||||
|
|
||||||
for (int i = 0; i < string.size(); ++i)
|
for (ByteArray::size_type i = 0; i < string.size(); ++i)
|
||||||
{
|
{
|
||||||
switch (string[i])
|
switch (string[i])
|
||||||
{
|
{
|
||||||
@ -50,48 +50,48 @@ QByteArray quotedString(const QByteArray &string)
|
|||||||
out.append("\\\\");
|
out.append("\\\\");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
out.append(string[i]);
|
out.push_back(string[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append('"');
|
out.push_back('"');
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray unquotedString(const QByteArray &string)
|
ByteArray unquotedString(const ByteArray& string)
|
||||||
{
|
{
|
||||||
if (string.size() < 2 || string[0] != '"')
|
if (string.size() < 2 || string[0] != '"')
|
||||||
return string;
|
return string;
|
||||||
|
|
||||||
QByteArray out;
|
ByteArray out;
|
||||||
out.reserve(string.size() - 2);
|
out.reserve(string.size() - 2);
|
||||||
|
|
||||||
for (int i = 1; i < string.size(); ++i)
|
for (ByteArray::size_type i = 1; i < string.size(); ++i)
|
||||||
{
|
{
|
||||||
switch (string[i])
|
switch (string[i])
|
||||||
{
|
{
|
||||||
case '\\':
|
case '\\':
|
||||||
if (++i < string.size())
|
if (++i < string.size())
|
||||||
out.append(string[i]);
|
out.push_back(string[i]);
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
return out;
|
return out;
|
||||||
default:
|
default:
|
||||||
out.append(string[i]);
|
out.push_back(string[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator)
|
std::list<ByteArray> splitQuotedStrings(const ByteArray &input, char separator)
|
||||||
{
|
{
|
||||||
QList<QByteArray> out;
|
std::list<ByteArray> out;
|
||||||
bool inquote = false;
|
bool inquote = false;
|
||||||
int start = 0;
|
ByteArray::size_type start = 0;
|
||||||
|
|
||||||
for (int i = 0; i < input.size(); ++i)
|
for (ByteArray::size_type i = 0; i < input.size(); ++i)
|
||||||
{
|
{
|
||||||
switch (input[i])
|
switch (input[i])
|
||||||
{
|
{
|
||||||
@ -106,13 +106,13 @@ QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator)
|
|||||||
|
|
||||||
if (!inquote && input[i] == separator)
|
if (!inquote && input[i] == separator)
|
||||||
{
|
{
|
||||||
out.append(input.mid(start, i - start));
|
out.push_back(input.mid(start, i - start));
|
||||||
start = i+1;
|
start = i+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start < input.size())
|
if (start < input.size())
|
||||||
out.append(input.mid(start));
|
out.push_back(input.mid(start));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
@ -33,14 +33,15 @@
|
|||||||
#ifndef STRINGUTIL_H
|
#ifndef STRINGUTIL_H
|
||||||
#define STRINGUTIL_H
|
#define STRINGUTIL_H
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <list>
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
QByteArray quotedString(const QByteArray &string);
|
#include "bytearray.h"
|
||||||
|
|
||||||
|
ByteArray quotedString(const ByteArray &string);
|
||||||
|
|
||||||
/* Return the unquoted contents of a string, either until an end quote or an unescaped separator character. */
|
/* Return the unquoted contents of a string, either until an end quote or an unescaped separator character. */
|
||||||
QByteArray unquotedString(const QByteArray &string);
|
ByteArray unquotedString(const ByteArray &string);
|
||||||
|
|
||||||
QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator);
|
std::list<ByteArray> splitQuotedStrings(const ByteArray& input, char separator);
|
||||||
|
|
||||||
#endif // STRINGUTIL_H
|
#endif // STRINGUTIL_H
|
793
libretroshare/src/tor/TorControl.cpp
Normal file
793
libretroshare/src/tor/TorControl.cpp
Normal file
@ -0,0 +1,793 @@
|
|||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the names of the copyright owners nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include "util/rsdir.h"
|
||||||
|
|
||||||
|
#include "retroshare/rstor.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include "TorControlSocket.h"
|
||||||
|
#include "HiddenService.h"
|
||||||
|
#include "ProtocolInfoCommand.h"
|
||||||
|
#include "AuthenticateCommand.h"
|
||||||
|
#include "SetConfCommand.h"
|
||||||
|
#include "GetConfCommand.h"
|
||||||
|
#include "AddOnionCommand.h"
|
||||||
|
#include "StrUtil.h"
|
||||||
|
#include "PendingOperation.h"
|
||||||
|
|
||||||
|
class nullstream: public std::ostream {};
|
||||||
|
|
||||||
|
static std::ostream& torctrldebug()
|
||||||
|
{
|
||||||
|
static nullstream null ;
|
||||||
|
|
||||||
|
if(true)
|
||||||
|
return std::cerr << time(NULL) << ":TOR CONTROL: " ;
|
||||||
|
else
|
||||||
|
return null ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define torCtrlDebug torctrldebug
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
TorControl::TorControl()
|
||||||
|
: mControlPort(0),mSocksPort(0),mStatus(NotConnected), mTorStatus(TorUnknown),mHasOwnership(false)
|
||||||
|
{
|
||||||
|
mSocket = new TorControlSocket(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl::~TorControl()
|
||||||
|
{
|
||||||
|
delete(mSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RsTorConnectivityStatus torConnectivityStatus(Tor::TorControl::Status t)
|
||||||
|
{
|
||||||
|
switch(t)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case TorControl::Error: return RsTorConnectivityStatus::ERROR;
|
||||||
|
case TorControl::NotConnected: return RsTorConnectivityStatus::NOT_CONNECTED;
|
||||||
|
case TorControl::Connecting: return RsTorConnectivityStatus::CONNECTING;
|
||||||
|
case TorControl::SocketConnected: return RsTorConnectivityStatus::SOCKET_CONNECTED;
|
||||||
|
case TorControl::Authenticating: return RsTorConnectivityStatus::AUTHENTICATING;
|
||||||
|
case TorControl::Authenticated: return RsTorConnectivityStatus::AUTHENTICATED;
|
||||||
|
case TorControl::HiddenServiceReady: return RsTorConnectivityStatus::HIDDEN_SERVICE_READY;
|
||||||
|
case TorControl::Unknown: return RsTorConnectivityStatus::UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static RsTorStatus torStatus(Tor::TorControl::TorStatus t)
|
||||||
|
{
|
||||||
|
switch(t)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case TorControl::TorUnknown: return RsTorStatus::UNKNOWN;
|
||||||
|
case TorControl::TorOffline: return RsTorStatus::OFFLINE;
|
||||||
|
case TorControl::TorReady: return RsTorStatus::READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::setStatus(TorControl::Status n)
|
||||||
|
{
|
||||||
|
if (n == mStatus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TorControl::Status old = mStatus;
|
||||||
|
mStatus = n;
|
||||||
|
|
||||||
|
if (old == TorControl::Error)
|
||||||
|
mErrorMessage.clear();
|
||||||
|
|
||||||
|
std::cerr << "Setting status to s=" << mStatus << " val=" << (int)torConnectivityStatus(mStatus) << std::endl;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::TOR_STATUS_CHANGED;
|
||||||
|
ev->mTorStatus = ::torStatus(mTorStatus);
|
||||||
|
ev->mTorConnectivityStatus = torConnectivityStatus(mStatus);
|
||||||
|
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
mStatusChanged_callback(mStatus, old);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::setTorStatus(TorControl::TorStatus n)
|
||||||
|
{
|
||||||
|
if (n == mTorStatus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RsDbg() << "Setting TorStatus=" << n ;
|
||||||
|
mTorStatus = n;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::TOR_STATUS_CHANGED;
|
||||||
|
ev->mTorStatus = ::torStatus(mTorStatus);
|
||||||
|
ev->mTorConnectivityStatus = torConnectivityStatus(mStatus);
|
||||||
|
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::setError(const std::string &message)
|
||||||
|
{
|
||||||
|
mErrorMessage = message;
|
||||||
|
setStatus(TorControl::Error);
|
||||||
|
|
||||||
|
RsWarn() << "torctrl: Error:" << mErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl::Status TorControl::status() const
|
||||||
|
{
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl::TorStatus TorControl::torStatus() const
|
||||||
|
{
|
||||||
|
return mTorStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorControl::torVersion() const
|
||||||
|
{
|
||||||
|
return mTorVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorControl::errorMessage() const
|
||||||
|
{
|
||||||
|
return mErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorControl::hasConnectivity() const
|
||||||
|
{
|
||||||
|
return torStatus() == TorReady && !mSocksAddress.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorControl::socksAddress() const
|
||||||
|
{
|
||||||
|
return mSocksAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t TorControl::socksPort() const
|
||||||
|
{
|
||||||
|
return mSocksPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<HiddenService*> TorControl::hiddenServices() const
|
||||||
|
{
|
||||||
|
return mServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string,std::string> TorControl::bootstrapStatus() const
|
||||||
|
{
|
||||||
|
return mBootstrapStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::setAuthPassword(const ByteArray &password)
|
||||||
|
{
|
||||||
|
mAuthPassword = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::connect(const std::string &address, uint16_t port)
|
||||||
|
{
|
||||||
|
if (status() > Connecting)
|
||||||
|
{
|
||||||
|
torCtrlDebug() << "Ignoring TorControl::connect due to existing connection" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTorAddress = address;
|
||||||
|
mControlPort = port;
|
||||||
|
setTorStatus(TorUnknown);
|
||||||
|
|
||||||
|
if(mSocket->isRunning())
|
||||||
|
mSocket->fullstop();
|
||||||
|
|
||||||
|
setStatus(Connecting);
|
||||||
|
|
||||||
|
if(mSocket->connectToHost(address, port))
|
||||||
|
{
|
||||||
|
setStatus(SocketConnected);
|
||||||
|
setTorStatus(TorOffline); // connected and running, but not yet ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::reconnect()
|
||||||
|
{
|
||||||
|
assert(!mTorAddress.empty() && mControlPort);
|
||||||
|
|
||||||
|
if (mTorAddress.empty() || !mControlPort || status() >= Connecting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setStatus(Connecting);
|
||||||
|
mSocket->connectToHost(mTorAddress, mControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::authenticateReply(TorControlCommand *sender)
|
||||||
|
{
|
||||||
|
AuthenticateCommand *command = dynamic_cast<AuthenticateCommand*>(sender);
|
||||||
|
assert(command);
|
||||||
|
assert(mStatus == TorControl::Authenticating);
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!command->isSuccessful()) {
|
||||||
|
setError(command->errorMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
torCtrlDebug() << "torctrl: Authentication successful" << std::endl;
|
||||||
|
setStatus(TorControl::Authenticated);
|
||||||
|
|
||||||
|
TorControlCommand *clientEvents = new TorControlCommand;
|
||||||
|
clientEvents->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data);});
|
||||||
|
|
||||||
|
mSocket->registerEvent(ByteArray("STATUS_CLIENT"), clientEvents);
|
||||||
|
|
||||||
|
getTorInfo();
|
||||||
|
publishServices();
|
||||||
|
|
||||||
|
// XXX Fix old configurations that would store unwanted options in torrc.
|
||||||
|
// This can be removed some suitable amount of time after 1.0.4.
|
||||||
|
if (mHasOwnership)
|
||||||
|
saveConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TorControl::authenticate()
|
||||||
|
{
|
||||||
|
assert(mStatus == TorControl::SocketConnected);
|
||||||
|
|
||||||
|
setStatus(TorControl::Authenticating);
|
||||||
|
torCtrlDebug() << "torctrl: Connected socket; querying information for authentication" << std::endl;
|
||||||
|
|
||||||
|
ProtocolInfoCommand *command = new ProtocolInfoCommand(this);
|
||||||
|
|
||||||
|
command->set_finished_callback( [this](TorControlCommand *sender) { protocolInfoReply(sender); });
|
||||||
|
command->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data); });
|
||||||
|
|
||||||
|
mSocket->sendCommand(command, command->build());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::socketDisconnected()
|
||||||
|
{
|
||||||
|
/* Clear some internal state */
|
||||||
|
mTorVersion.clear();
|
||||||
|
mSocksAddress.clear();
|
||||||
|
mSocksPort = 0;
|
||||||
|
setTorStatus(TorControl::TorUnknown);
|
||||||
|
|
||||||
|
/* This emits the disconnected() signal as well */
|
||||||
|
setStatus(TorControl::NotConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::socketError(const std::string& s)
|
||||||
|
{
|
||||||
|
setError("Connection failed: " + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::protocolInfoReply(TorControlCommand *sender)
|
||||||
|
{
|
||||||
|
ProtocolInfoCommand *info = dynamic_cast<ProtocolInfoCommand*>(sender);
|
||||||
|
if (!info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mTorVersion = info->torVersion();
|
||||||
|
|
||||||
|
if (mStatus == TorControl::Authenticating)
|
||||||
|
{
|
||||||
|
AuthenticateCommand *auth = new AuthenticateCommand;
|
||||||
|
|
||||||
|
auth->set_finished_callback( [this](TorControlCommand *sender) { authenticateReply(sender); });
|
||||||
|
|
||||||
|
ByteArray data;
|
||||||
|
ProtocolInfoCommand::AuthMethod methods = info->authMethods();
|
||||||
|
|
||||||
|
if(methods & ProtocolInfoCommand::AuthNull)
|
||||||
|
{
|
||||||
|
torCtrlDebug() << "torctrl: Using null authentication" << std::endl;
|
||||||
|
data = auth->build();
|
||||||
|
}
|
||||||
|
else if ((methods & ProtocolInfoCommand::AuthCookie) && !info->cookieFile().empty())
|
||||||
|
{
|
||||||
|
std::string cookieFile = info->cookieFile();
|
||||||
|
std::string cookieError;
|
||||||
|
torCtrlDebug() << "torctrl: Using cookie authentication with file" << cookieFile << std::endl;
|
||||||
|
|
||||||
|
FILE *f = fopen(cookieFile.c_str(),"r");
|
||||||
|
|
||||||
|
if(f)
|
||||||
|
{
|
||||||
|
std::string cookie;
|
||||||
|
char c;
|
||||||
|
while((c=getc(f))!=EOF)
|
||||||
|
cookie += c;
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
/* Simple test to avoid a vulnerability where any process listening on what we think is
|
||||||
|
* the control port could trick us into sending the contents of an arbitrary file */
|
||||||
|
if (cookie.size() == 32)
|
||||||
|
data = auth->build(cookie);
|
||||||
|
else
|
||||||
|
cookieError = "Unexpected file size";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cookieError = "Cannot open file " + cookieFile + ". errno=" + RsUtil::NumberToString(errno);
|
||||||
|
|
||||||
|
if (!cookieError.empty() || data.isNull())
|
||||||
|
{
|
||||||
|
/* If we know a password and password authentication is allowed, try using that instead.
|
||||||
|
* This is a strange corner case that will likely never happen in a normal configuration,
|
||||||
|
* but it has happened. */
|
||||||
|
if ((methods & ProtocolInfoCommand::AuthHashedPassword) && !mAuthPassword.empty())
|
||||||
|
{
|
||||||
|
torCtrlDebug() << "torctrl: Unable to read authentication cookie file:" << cookieError << std::endl;
|
||||||
|
goto usePasswordAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
setError("Unable to read authentication cookie file: " + cookieError);
|
||||||
|
delete auth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((methods & ProtocolInfoCommand::AuthHashedPassword) && !mAuthPassword.empty())
|
||||||
|
{
|
||||||
|
usePasswordAuth:
|
||||||
|
torCtrlDebug() << "torctrl: Using hashed password authentication with AuthPasswd=\"" << mAuthPassword.toString() << "\"" << std::endl;
|
||||||
|
data = auth->build(mAuthPassword);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (methods & ProtocolInfoCommand::AuthHashedPassword)
|
||||||
|
setError("Tor requires a control password to connect, but no password is configured.");
|
||||||
|
else
|
||||||
|
setError("Tor is not configured to accept any supported authentication methods.");
|
||||||
|
delete auth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSocket->sendCommand(auth, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::getTorInfo()
|
||||||
|
{
|
||||||
|
assert(isConnected());
|
||||||
|
|
||||||
|
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetInfo);
|
||||||
|
//connect(command, &TorControlCommand::finished, this, &TorControl::getTorInfoReply);
|
||||||
|
command->set_finished_callback( [this](TorControlCommand *sender) { getTorInfoReply(sender); });
|
||||||
|
command->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data); });
|
||||||
|
|
||||||
|
std::list<std::string> keys{ "status/circuit-established","status/bootstrap-phase" };
|
||||||
|
|
||||||
|
keys.push_back("net/listeners/socks");
|
||||||
|
|
||||||
|
mSocket->sendCommand(command, command->build(keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::getTorInfoReply(TorControlCommand *sender)
|
||||||
|
{
|
||||||
|
GetConfCommand *command = dynamic_cast<GetConfCommand*>(sender);
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::list<ByteArray> listenAddresses = splitQuotedStrings(command->get("net/listeners/socks").front(), ' ');
|
||||||
|
|
||||||
|
for (const auto& add:listenAddresses) {
|
||||||
|
ByteArray value = unquotedString(add);
|
||||||
|
int sepp = value.indexOf(':');
|
||||||
|
std::string address(value.mid(0, sepp).toString());
|
||||||
|
uint16_t port = (uint16_t)value.mid(sepp+1).toInt();
|
||||||
|
|
||||||
|
/* Use the first address that matches the one used for this control connection. If none do,
|
||||||
|
* just use the first address and rely on the user to reconfigure if necessary (not a problem;
|
||||||
|
* their setup is already very customized) */
|
||||||
|
if (mSocksAddress.empty() || address == mSocket->peerAddress()) {
|
||||||
|
mSocksAddress = address;
|
||||||
|
mSocksPort = port;
|
||||||
|
if (address == mSocket->peerAddress())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It is not immediately an error to have no SOCKS address; when DisableNetwork is set there won't be a
|
||||||
|
* listener yet. To handle that situation, we'll try to read the socks address again when TorReady state
|
||||||
|
* is reached. */
|
||||||
|
if (!mSocksAddress.empty()) {
|
||||||
|
torCtrlDebug() << "torctrl: SOCKS address is " << mSocksAddress << ":" << mSocksPort << std::endl;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::TOR_CONNECTIVITY_CHANGED;
|
||||||
|
ev->mTorConnectivityStatus = torConnectivityStatus(mStatus);
|
||||||
|
ev->mTorStatus = ::torStatus(mTorStatus);
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ByteArray(command->get("status/circuit-established").front()).toInt() == 1)
|
||||||
|
{
|
||||||
|
torCtrlDebug() << "torctrl: Tor indicates that circuits have been established; state is TorReady" << std::endl;
|
||||||
|
setTorStatus(TorControl::TorReady);
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// setTorStatus(TorControl::TorOffline);
|
||||||
|
|
||||||
|
auto bootstrap = command->get("status/bootstrap-phase");
|
||||||
|
if (!bootstrap.empty())
|
||||||
|
updateBootstrap(splitQuotedStrings(bootstrap.front(), ' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::addHiddenService(HiddenService *service)
|
||||||
|
{
|
||||||
|
if (std::find(mServices.begin(),mServices.end(),service) != mServices.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mServices.push_back(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::publishServices()
|
||||||
|
{
|
||||||
|
torCtrlDebug() << "Publish Services... " ;
|
||||||
|
|
||||||
|
assert(isConnected());
|
||||||
|
if (mServices.empty())
|
||||||
|
{
|
||||||
|
std::cerr << "No service regstered!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
|
if (torVersionAsNewAs("0.2.7")) {
|
||||||
|
for(HiddenService *service: mServices)
|
||||||
|
{
|
||||||
|
if (service->hostname().empty())
|
||||||
|
torCtrlDebug() << "torctrl: Creating a new hidden service" << std::endl;
|
||||||
|
else
|
||||||
|
torCtrlDebug() << "torctrl: Publishing hidden service: " << service->hostname() << std::endl;
|
||||||
|
AddOnionCommand *onionCommand = new AddOnionCommand(service);
|
||||||
|
//protocolInfoReplyQObject::connect(onionCommand, &AddOnionCommand::succeeded, service, &HiddenService::servicePublished);
|
||||||
|
onionCommand->set_succeeded_callback( [this,service]() { checkHiddenService(service) ; });
|
||||||
|
mSocket->sendCommand(onionCommand, onionCommand->build());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
torCtrlDebug() << "torctrl: Using legacy SETCONF hidden service configuration for tor" << mTorVersion << std::endl;
|
||||||
|
SetConfCommand *command = new SetConfCommand;
|
||||||
|
std::list<std::pair<std::string,std::string> > torConfig;
|
||||||
|
|
||||||
|
for(HiddenService *service: mServices)
|
||||||
|
{
|
||||||
|
if (service->dataPath().empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (service->privateKey().isLoaded() && !RsDirUtil::fileExists(service->dataPath() + "/private_key")) {
|
||||||
|
// This case can happen if tor is downgraded after the profile is created
|
||||||
|
RsWarn() << "Cannot publish ephemeral hidden services with this version of tor; skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
torCtrlDebug() << "torctrl: Configuring hidden service at" << service->dataPath() << std::endl;
|
||||||
|
|
||||||
|
torConfig.push_back(std::make_pair("HiddenServiceDir", service->dataPath()));
|
||||||
|
|
||||||
|
const std::list<HiddenService::Target> &targets = service->targets();
|
||||||
|
for (auto tit:targets)
|
||||||
|
{
|
||||||
|
std::string target = RsUtil::NumberToString(tit.servicePort) + " "
|
||||||
|
+tit.targetAddress + ":"
|
||||||
|
+RsUtil::NumberToString(tit.targetPort);
|
||||||
|
torConfig.push_back(std::make_pair("HiddenServicePort", target));
|
||||||
|
}
|
||||||
|
|
||||||
|
command->set_ConfSucceeded_callback( [this,service]() { checkHiddenService(service); });
|
||||||
|
//QObject::connect(command, &SetConfCommand::setConfSucceeded, service, &HiddenService::servicePublished);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!torConfig.empty())
|
||||||
|
mSocket->sendCommand(command, command->build(torConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::checkHiddenService(HiddenService *service)
|
||||||
|
{
|
||||||
|
service->servicePublished();
|
||||||
|
|
||||||
|
if(service->status() == HiddenService::Online)
|
||||||
|
{
|
||||||
|
RsDbg() << "Hidden service published and ready!" ;
|
||||||
|
|
||||||
|
setStatus(TorControl::HiddenServiceReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::shutdown()
|
||||||
|
{
|
||||||
|
if (!hasOwnership()) {
|
||||||
|
RsWarn() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSocket->sendCommand(ByteArray("SIGNAL SHUTDOWN\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::shutdownSync()
|
||||||
|
{
|
||||||
|
if (!hasOwnership()) {
|
||||||
|
RsWarn() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown();
|
||||||
|
while (mSocket->moretowrite(0))
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
mSocket->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::statusEvent(int /* code */, const ByteArray &data)
|
||||||
|
{
|
||||||
|
std::list<ByteArray> tokens = splitQuotedStrings(data.trimmed(), ' ');
|
||||||
|
if (tokens.size() < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ByteArray& tok2 = *(++(++tokens.begin()));
|
||||||
|
torCtrlDebug() << "torctrl: status event:" << data.trimmed().toString() << " tok2=\"" << tok2.toString() << "\"" << std::endl;
|
||||||
|
|
||||||
|
if (tok2 == "CIRCUIT_ESTABLISHED")
|
||||||
|
setTorStatus(TorControl::TorReady);
|
||||||
|
else if (tok2 == "CIRCUIT_NOT_ESTABLISHED")
|
||||||
|
setTorStatus(TorControl::TorOffline);
|
||||||
|
else if (tok2 == "BOOTSTRAP")
|
||||||
|
{
|
||||||
|
tokens.pop_front();
|
||||||
|
updateBootstrap(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::updateBootstrap(const std::list<ByteArray> &data)
|
||||||
|
{
|
||||||
|
std::cerr << "********** Updating bootstrap status ************" << std::endl;
|
||||||
|
mBootstrapStatus.clear();
|
||||||
|
// WARN or NOTICE
|
||||||
|
mBootstrapStatus["severity"] = (*data.begin()).toString();
|
||||||
|
|
||||||
|
auto dat = data.begin();
|
||||||
|
++dat;
|
||||||
|
|
||||||
|
for(;dat!=data.end();++dat) { // for(int i = 1; i < data.size(); i++) {
|
||||||
|
int equals = (*dat).indexOf('=');
|
||||||
|
ByteArray key = (*dat).mid(0, equals);
|
||||||
|
ByteArray value;
|
||||||
|
|
||||||
|
if (equals >= 0)
|
||||||
|
value = unquotedString((*dat).mid(equals + 1));
|
||||||
|
|
||||||
|
mBootstrapStatus[key.toLower().toString()] = value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::BOOTSTRAP_STATUS_CHANGED;
|
||||||
|
ev->mTorConnectivityStatus = torConnectivityStatus(mStatus);
|
||||||
|
ev->mTorStatus = ::torStatus(mTorStatus);
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlCommand *TorControl::getConfiguration(const std::string& options)
|
||||||
|
{
|
||||||
|
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetConf);
|
||||||
|
command->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data); });
|
||||||
|
mSocket->sendCommand(command, command->build(options));
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlCommand *TorControl::setConfiguration(const std::list<std::pair<std::string,std::string> >& options)
|
||||||
|
{
|
||||||
|
SetConfCommand *command = new SetConfCommand;
|
||||||
|
command->setResetMode(true);
|
||||||
|
mSocket->sendCommand(command, command->build(options));
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
|
||||||
|
class SaveConfigOperation : public PendingOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SaveConfigOperation()
|
||||||
|
: PendingOperation(), command(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void start(TorControlSocket *socket)
|
||||||
|
{
|
||||||
|
assert(!command);
|
||||||
|
command = new GetConfCommand(GetConfCommand::GetInfo);
|
||||||
|
command->set_finished_callback([this](TorControlCommand *sender){ configTextReply(sender); });
|
||||||
|
|
||||||
|
socket->sendCommand(command, command->build(std::list<std::string> { "config-text" , "config-file" } ));
|
||||||
|
}
|
||||||
|
|
||||||
|
void configTextReply(TorControlCommand * /*sender*/)
|
||||||
|
{
|
||||||
|
assert(command);
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto lpath = command->get("config-file");
|
||||||
|
std::string path = (lpath.empty()?std::string():lpath.front());
|
||||||
|
|
||||||
|
if (path.empty()) {
|
||||||
|
finishWithError("Cannot write torrc without knowing its path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out of paranoia, refuse to write any file not named 'torrc', or if the
|
||||||
|
// file doesn't exist
|
||||||
|
|
||||||
|
auto filename = RsDirUtil::getFileName(path);
|
||||||
|
|
||||||
|
if(filename != "torrc" || !RsDirUtil::fileExists(path))
|
||||||
|
{
|
||||||
|
finishWithError("Refusing to write torrc to unacceptable path " + path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream file(path);
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
finishWithError("Failed opening torrc file for writing: permissions error?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove these keys when writing torrc; they are set at runtime and contain
|
||||||
|
// absolute paths or port numbers
|
||||||
|
static const char *bannedKeys[] = {
|
||||||
|
"ControlPortWriteToFile",
|
||||||
|
"DataDirectory",
|
||||||
|
"HiddenServiceDir",
|
||||||
|
"HiddenServicePort",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
auto configText = command->get("config-text") ;
|
||||||
|
|
||||||
|
for(const auto& value: configText)
|
||||||
|
{
|
||||||
|
ByteArray line(value);
|
||||||
|
|
||||||
|
bool skip = false;
|
||||||
|
for (const char **key = bannedKeys; *key; key++) {
|
||||||
|
if (line.startsWith(*key)) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
file << line.toString() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
torCtrlDebug() << "torctrl: Wrote torrc file" << std::endl;
|
||||||
|
finishWithSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GetConfCommand *command;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingOperation *TorControl::saveConfiguration()
|
||||||
|
{
|
||||||
|
if (!hasOwnership()) {
|
||||||
|
RsWarn() << "torctrl: Ignoring save configuration command for a tor instance I don't own";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveConfigOperation *operation = new SaveConfigOperation();
|
||||||
|
|
||||||
|
operation->set_finished_callback( [operation]() { delete operation; });
|
||||||
|
operation->start(mSocket);
|
||||||
|
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorControl::hasOwnership() const
|
||||||
|
{
|
||||||
|
return mHasOwnership;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControl::takeOwnership()
|
||||||
|
{
|
||||||
|
mHasOwnership = true;
|
||||||
|
mSocket->sendCommand(ByteArray("TAKEOWNERSHIP\r\n"));
|
||||||
|
|
||||||
|
// Reset PID-based polling
|
||||||
|
std::list<std::pair<std::string,std::string> > options;
|
||||||
|
options.push_back(std::make_pair("__OwningControllerProcess",std::string()));
|
||||||
|
setConfiguration(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorControl::torVersionAsNewAs(const std::string& match) const
|
||||||
|
{
|
||||||
|
auto split = ByteArray(torVersion()).split(ByteArray(".-"));
|
||||||
|
auto matchSplit = ByteArray(match).split(ByteArray(".-"));
|
||||||
|
|
||||||
|
int split_size = split.size();
|
||||||
|
auto b_split(split.begin());
|
||||||
|
auto b_matchsplit(matchSplit.begin());
|
||||||
|
|
||||||
|
for(int i=0;;)
|
||||||
|
{
|
||||||
|
int currentVal,matchVal;
|
||||||
|
bool ok1 = RsUtil::StringToInt((*b_split).toString(),currentVal);
|
||||||
|
bool ok2 = RsUtil::StringToInt((*b_matchsplit).toString(),matchVal);
|
||||||
|
|
||||||
|
if (!ok1 || !ok2)
|
||||||
|
return false;
|
||||||
|
if (currentVal > matchVal)
|
||||||
|
return true;
|
||||||
|
if (currentVal < matchVal)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
++i;
|
||||||
|
|
||||||
|
if(i >= split_size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
++b_split;
|
||||||
|
++b_matchsplit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Versions are equal, up to the length of match
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
152
libretroshare/src/tor/TorControl.h
Normal file
152
libretroshare/src/tor/TorControl.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the names of the copyright owners nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "PendingOperation.h"
|
||||||
|
#include "bytearray.h"
|
||||||
|
#include "TorControlSocket.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class HiddenService;
|
||||||
|
class TorControlSocket;
|
||||||
|
class TorControlCommand;
|
||||||
|
|
||||||
|
class TorControl : public TorControlSocketClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
Error = 0x00,
|
||||||
|
NotConnected = 0x01,
|
||||||
|
Connecting = 0x02,
|
||||||
|
SocketConnected = 0x03,
|
||||||
|
Authenticating = 0x04,
|
||||||
|
Authenticated = 0x05,
|
||||||
|
HiddenServiceReady = 0x06,
|
||||||
|
Unknown = 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TorStatus
|
||||||
|
{
|
||||||
|
TorUnknown = 0x00,
|
||||||
|
TorOffline = 0x01,
|
||||||
|
TorReady = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit TorControl();
|
||||||
|
virtual ~TorControl();
|
||||||
|
|
||||||
|
/* Information */
|
||||||
|
Status status() const;
|
||||||
|
TorStatus torStatus() const;
|
||||||
|
std::string torVersion() const;
|
||||||
|
bool torVersionAsNewAs(const std::string &version) const;
|
||||||
|
std::string errorMessage() const;
|
||||||
|
|
||||||
|
bool hasConnectivity() const;
|
||||||
|
std::string socksAddress() const;
|
||||||
|
uint16_t socksPort() const;
|
||||||
|
|
||||||
|
/* Authentication */
|
||||||
|
void setAuthPassword(const ByteArray& password);
|
||||||
|
|
||||||
|
/* Connection */
|
||||||
|
bool isConnected() const { return status() >= Authenticated; }
|
||||||
|
void connect(const std::string &address, uint16_t port);
|
||||||
|
void authenticate();
|
||||||
|
|
||||||
|
/* Ownership means that tor is managed by this socket, and we
|
||||||
|
* can shut it down, own its configuration, etc. */
|
||||||
|
bool hasOwnership() const;
|
||||||
|
void takeOwnership();
|
||||||
|
|
||||||
|
/* Hidden Services */
|
||||||
|
std::list<HiddenService*> hiddenServices() const;
|
||||||
|
void addHiddenService(HiddenService *service);
|
||||||
|
|
||||||
|
std::map<std::string, std::string> bootstrapStatus() const;
|
||||||
|
TorControlCommand *getConfiguration(const std::string &options);
|
||||||
|
TorControlCommand *setConfiguration(const std::list<std::pair<std::string, std::string> > &options);
|
||||||
|
PendingOperation *saveConfiguration();
|
||||||
|
|
||||||
|
void set_statusChanged_callback(const std::function<void(int,int)>& f) { mStatusChanged_callback = f ;}
|
||||||
|
void set_connected_callback(const std::function<void(void)>& f) { mConnected_callback = f ;}
|
||||||
|
void set_disconnected_callback(const std::function<void(void)>& f) { mDisconnected_callback = f ;}
|
||||||
|
|
||||||
|
virtual void socketError(const std::string &s) override;
|
||||||
|
|
||||||
|
/* Instruct Tor to shutdown */
|
||||||
|
void shutdown();
|
||||||
|
/* Call shutdown(), and wait synchronously for the command to be written */
|
||||||
|
void shutdownSync();
|
||||||
|
|
||||||
|
void reconnect();
|
||||||
|
|
||||||
|
void getTorInfo();
|
||||||
|
private:
|
||||||
|
TorControlSocket *mSocket;
|
||||||
|
std::string mTorAddress;
|
||||||
|
std::string mErrorMessage;
|
||||||
|
std::string mTorVersion;
|
||||||
|
ByteArray mAuthPassword;
|
||||||
|
std::string mSocksAddress;
|
||||||
|
std::list<HiddenService*> mServices;
|
||||||
|
uint16_t mControlPort, mSocksPort;
|
||||||
|
TorControl::Status mStatus;
|
||||||
|
TorControl::TorStatus mTorStatus;
|
||||||
|
std::map<std::string,std::string> mBootstrapStatus;
|
||||||
|
bool mHasOwnership;
|
||||||
|
|
||||||
|
void checkHiddenService(HiddenService *service);
|
||||||
|
void getTorInfoReply(TorControlCommand *sender);
|
||||||
|
void setStatus(TorControl::Status n);
|
||||||
|
void statusEvent(int code, const ByteArray &data);
|
||||||
|
void setTorStatus(TorControl::TorStatus n);
|
||||||
|
void updateBootstrap(const std::list<ByteArray>& data);
|
||||||
|
void setError(const std::string& message);
|
||||||
|
void publishServices();
|
||||||
|
void protocolInfoReply(TorControlCommand *sender);
|
||||||
|
void socketDisconnected();
|
||||||
|
void authenticateReply(TorControlCommand *sender);
|
||||||
|
|
||||||
|
std::function<void(int,int)> mStatusChanged_callback;
|
||||||
|
std::function<void(void)> mConnected_callback;
|
||||||
|
std::function<void(void)> mDisconnected_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -31,33 +31,33 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
TorControlCommand::TorControlCommand()
|
TorControlCommand::TorControlCommand()
|
||||||
: m_finalStatus(0)
|
: m_finalStatus(0),
|
||||||
|
mReplyLine ( std::function<void(int, const ByteArray &)>([](int, const ByteArray &){})),
|
||||||
|
mFinished ( std::function<void(TorControlCommand*)>([](TorControlCommand*){}))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlCommand::onReply(int statusCode, const QByteArray &data)
|
void TorControlCommand::onReply(int statusCode, const ByteArray &data)
|
||||||
{
|
{
|
||||||
emit replyLine(statusCode, data);
|
mReplyLine(statusCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlCommand::onFinished(int statusCode)
|
void TorControlCommand::onFinished(int statusCode)
|
||||||
{
|
{
|
||||||
m_finalStatus = statusCode;
|
m_finalStatus = statusCode;
|
||||||
emit finished();
|
mFinished(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlCommand::onDataLine(const QByteArray &data)
|
void TorControlCommand::onDataLine(const ByteArray &data)
|
||||||
{
|
{
|
||||||
Q_UNUSED(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlCommand::onDataFinished()
|
void TorControlCommand::onDataFinished()
|
||||||
{
|
{
|
||||||
qWarning() << "torctrl: Unexpected data response for command";
|
RsWarn() << "torctrl: Unexpected data response for command";
|
||||||
}
|
}
|
||||||
|
|
@ -30,41 +30,44 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TORCONTROLCOMMAND_H
|
#pragma once
|
||||||
#define TORCONTROLCOMMAND_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <functional>
|
||||||
#include <QByteArray>
|
#include "bytearray.h"
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
|
|
||||||
class TorControlCommand : public QObject
|
class ProtocolInfoCommand;
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(TorControlCommand)
|
|
||||||
|
|
||||||
|
class TorControlCommand
|
||||||
|
{
|
||||||
friend class TorControlSocket;
|
friend class TorControlSocket;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorControlCommand();
|
TorControlCommand();
|
||||||
|
virtual ~TorControlCommand()=default;
|
||||||
|
|
||||||
int statusCode() const { return m_finalStatus; }
|
int statusCode() const { return m_finalStatus; }
|
||||||
|
|
||||||
signals:
|
void set_replyLine_callback( const std::function<void(int statusCode, const ByteArray &data)>& f) { mReplyLine=f ; }
|
||||||
void replyLine(int statusCode, const QByteArray &data);
|
void set_finished_callback( const std::function<void(TorControlCommand *sender)>& f) { mFinished=f; };
|
||||||
void finished();
|
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
virtual void onReply(int statusCode, const QByteArray &data);
|
virtual void onReply(int statusCode, const ByteArray &data);
|
||||||
virtual void onFinished(int statusCode);
|
virtual void onFinished(int statusCode);
|
||||||
virtual void onDataLine(const QByteArray &data);
|
virtual void onDataLine(const ByteArray &data);
|
||||||
virtual void onDataFinished();
|
virtual void onDataFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_finalStatus;
|
int m_finalStatus;
|
||||||
|
|
||||||
|
// Disable copy
|
||||||
|
TorControlCommand(const TorControlCommand&){}
|
||||||
|
TorControlCommand& operator=(const TorControlCommand&){ return *this; }
|
||||||
|
|
||||||
|
std::function<void(int statusCode, const ByteArray &data)> mReplyLine;
|
||||||
|
std::function<void(TorControlCommand *sender)> mFinished;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // TORCONTROLCOMMAND_H
|
|
@ -34,15 +34,12 @@
|
|||||||
|
|
||||||
#include "TorControlSocket.h"
|
#include "TorControlSocket.h"
|
||||||
#include "TorControlCommand.h"
|
#include "TorControlCommand.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace Tor;
|
using namespace Tor;
|
||||||
|
|
||||||
TorControlSocket::TorControlSocket(QObject *parent)
|
TorControlSocket::TorControlSocket(TorControlSocketClient *client)
|
||||||
: QTcpSocket(parent), currentCommand(0), inDataReply(false)
|
: RsThreadedTcpSocket(),currentCommand(0), inDataReply(false),mClient(client)
|
||||||
{
|
{
|
||||||
connect(this, SIGNAL(readyRead()), this, SLOT(process()));
|
|
||||||
connect(this, SIGNAL(disconnected()), this, SLOT(clear()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TorControlSocket::~TorControlSocket()
|
TorControlSocket::~TorControlSocket()
|
||||||
@ -50,24 +47,43 @@ TorControlSocket::~TorControlSocket()
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlSocket::sendCommand(TorControlCommand *command, const QByteArray &data)
|
bool TorControlSocket::connectToHost(const std::string& tcp_address,uint16_t tcp_port)
|
||||||
{
|
{
|
||||||
Q_ASSERT(data.endsWith("\r\n"));
|
if(RsTcpSocket::connect(tcp_address,tcp_port))
|
||||||
|
{
|
||||||
|
start("TorControlSocket");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
commandQueue.append(command);
|
}
|
||||||
write(data);
|
std::string TorControlSocket::peerAddress() const
|
||||||
|
{
|
||||||
|
if(connectionState() == State::CONNECTED)
|
||||||
|
return connectAddress();
|
||||||
|
else
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
void TorControlSocket::sendCommand(TorControlCommand *command, const ByteArray& data)
|
||||||
|
{
|
||||||
|
assert(data.endsWith(ByteArray("\r\n")));
|
||||||
|
|
||||||
std::cerr << "[TOR CTRL] Sent: \"" << QString(data.trimmed()).toStdString() << "\"" << std::endl;
|
commandQueue.push_back(command);
|
||||||
|
senddata((void*)data.data(),data.size());
|
||||||
|
|
||||||
|
std::cerr << "[TOR CTRL] Sent: \"" << data.trimmed().toString() << "\"" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlSocket::registerEvent(const QByteArray &event, TorControlCommand *command)
|
void TorControlSocket::registerEvent(const ByteArray &event, TorControlCommand *command)
|
||||||
{
|
{
|
||||||
eventCommands.insert(event, command);
|
eventCommands.insert(std::make_pair(event, command));
|
||||||
|
|
||||||
QByteArray data("SETEVENTS");
|
ByteArray data("SETEVENTS");
|
||||||
foreach (const QByteArray &key, eventCommands.keys()) {
|
for(auto it:eventCommands)
|
||||||
|
{
|
||||||
data += ' ';
|
data += ' ';
|
||||||
data += key;
|
data += it.first;
|
||||||
}
|
}
|
||||||
data += "\r\n";
|
data += "\r\n";
|
||||||
|
|
||||||
@ -76,30 +92,53 @@ void TorControlSocket::registerEvent(const QByteArray &event, TorControlCommand
|
|||||||
|
|
||||||
void TorControlSocket::clear()
|
void TorControlSocket::clear()
|
||||||
{
|
{
|
||||||
qDeleteAll(commandQueue);
|
for(auto cmd:commandQueue) delete cmd;
|
||||||
commandQueue.clear();
|
commandQueue.clear();
|
||||||
qDeleteAll(eventCommands);
|
|
||||||
|
for(auto cmd:eventCommands) delete cmd.second;
|
||||||
eventCommands.clear();
|
eventCommands.clear();
|
||||||
|
|
||||||
inDataReply = false;
|
inDataReply = false;
|
||||||
currentCommand = 0;
|
currentCommand = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlSocket::setError(const QString &message)
|
void TorControlSocket::setError(const std::string &message)
|
||||||
{
|
{
|
||||||
m_errorMessage = message;
|
m_errorMessage = message;
|
||||||
emit error(message);
|
mClient->socketError(message);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ByteArray TorControlSocket::readline(int s)
|
||||||
|
{
|
||||||
|
ByteArray b(s);
|
||||||
|
int real_size;
|
||||||
|
|
||||||
|
if(! (real_size = RsTcpSocket::readline(b.data(),s)))
|
||||||
|
return ByteArray();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b.resize(real_size);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TorControlSocket::process()
|
void TorControlSocket::process()
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!canReadLine())
|
if (!moretoread(0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QByteArray line = readLine(5120);
|
ByteArray line = readline(5120);
|
||||||
if (!line.endsWith("\r\n")) {
|
|
||||||
setError(QStringLiteral("Invalid control message syntax"));
|
if(line.empty()) // This happens when the incoming buffer isn't empty yet doesn't have a full line already.
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.endsWith(ByteArray("\r\n"))) {
|
||||||
|
setError("Invalid control message syntax");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
line.chop(2);
|
line.chop(2);
|
||||||
@ -118,7 +157,7 @@ void TorControlSocket::process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (line.size() < 4) {
|
if (line.size() < 4) {
|
||||||
setError(QStringLiteral("Invalid control message syntax"));
|
setError("Invalid control message syntax");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +170,7 @@ void TorControlSocket::process()
|
|||||||
line = line.mid(4);
|
line = line.mid(4);
|
||||||
|
|
||||||
if (!isFinalReply && !inDataReply && type != '-') {
|
if (!isFinalReply && !inDataReply && type != '-') {
|
||||||
setError(QStringLiteral("Invalid control message syntax"));
|
setError("Invalid control message syntax");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,10 +179,15 @@ void TorControlSocket::process()
|
|||||||
if (!currentCommand) {
|
if (!currentCommand) {
|
||||||
int space = line.indexOf(' ');
|
int space = line.indexOf(' ');
|
||||||
if (space > 0)
|
if (space > 0)
|
||||||
currentCommand = eventCommands.value(line.mid(0, space));
|
{
|
||||||
|
auto it = eventCommands.find(line.mid(0, space).toString());
|
||||||
|
|
||||||
|
if(it != eventCommands.end())
|
||||||
|
currentCommand = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentCommand) {
|
if (!currentCommand) {
|
||||||
qWarning() << "torctrl: Ignoring unknown event";
|
RsWarn() << "torctrl: Ignoring unknown event";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,23 +200,36 @@ void TorControlSocket::process()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandQueue.isEmpty()) {
|
if (commandQueue.empty()) {
|
||||||
qWarning() << "torctrl: Received unexpected data";
|
RsWarn() << "torctrl: Received unexpected data";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorControlCommand *command = commandQueue.first();
|
TorControlCommand *command = commandQueue.front();
|
||||||
if (command)
|
if (command)
|
||||||
command->onReply(statusCode, line);
|
command->onReply(statusCode, line);
|
||||||
|
|
||||||
if (inDataReply) {
|
if (inDataReply) {
|
||||||
currentCommand = command;
|
currentCommand = command;
|
||||||
} else if (isFinalReply) {
|
} else if (isFinalReply) {
|
||||||
commandQueue.takeFirst();
|
commandQueue.pop_front();
|
||||||
if (command) {
|
if (command) {
|
||||||
command->onFinished(statusCode);
|
command->onFinished(statusCode);
|
||||||
command->deleteLater();
|
delete command; // should we "delete later" ?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TorControlSocket::tick()
|
||||||
|
{
|
||||||
|
bool rw = RsTcpSocket::tick();
|
||||||
|
|
||||||
|
if(moretoread(0))
|
||||||
|
process();
|
||||||
|
|
||||||
|
if(!rw)
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // temporisation when nothing happens
|
||||||
|
|
||||||
|
return 0; // not sure about what we should return here.
|
||||||
|
}
|
@ -30,48 +30,58 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TORCONTROLSOCKET_H
|
#pragma once
|
||||||
#define TORCONTROLSOCKET_H
|
|
||||||
|
|
||||||
#include <QTcpSocket>
|
#include "pqi/rstcpsocket.h"
|
||||||
#include <QQueue>
|
#include "bytearray.h"
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
|
|
||||||
class TorControlCommand;
|
class TorControlCommand;
|
||||||
|
|
||||||
class TorControlSocket : public QTcpSocket
|
class TorControlSocketClient
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit TorControlSocket(QObject *parent = 0);
|
virtual void socketError(const std::string& s) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TorControlSocket : public RsThreadedTcpSocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TorControlSocket(TorControlSocketClient *client);
|
||||||
virtual ~TorControlSocket();
|
virtual ~TorControlSocket();
|
||||||
|
|
||||||
QString errorMessage() const { return m_errorMessage; }
|
std::string errorMessage() const { return m_errorMessage; }
|
||||||
|
|
||||||
void registerEvent(const QByteArray &event, TorControlCommand *handler);
|
bool connectToHost(const std::string& tcp_address,uint16_t tcp_port);
|
||||||
|
void registerEvent(const ByteArray &event, TorControlCommand *handler);
|
||||||
|
|
||||||
void sendCommand(const QByteArray &data) { sendCommand(0, data); }
|
void sendCommand(const ByteArray& data) { sendCommand(0, data); }
|
||||||
void sendCommand(TorControlCommand *command, const QByteArray &data);
|
void sendCommand(TorControlCommand *command, const ByteArray &data);
|
||||||
|
|
||||||
signals:
|
ByteArray readline(int s);
|
||||||
void error(const QString &message);
|
|
||||||
|
// threaded TcpSocket
|
||||||
|
|
||||||
|
virtual int tick() override;
|
||||||
|
|
||||||
|
std::string peerAddress() const;
|
||||||
|
|
||||||
|
const std::string& errorString() const { return m_errorMessage ;}
|
||||||
|
|
||||||
private slots:
|
|
||||||
void process();
|
void process();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QQueue<TorControlCommand*> commandQueue;
|
std::list<TorControlCommand*> commandQueue;
|
||||||
QHash<QByteArray,TorControlCommand*> eventCommands;
|
std::map<ByteArray,TorControlCommand*> eventCommands;
|
||||||
QString m_errorMessage;
|
std::string m_errorMessage;
|
||||||
TorControlCommand *currentCommand;
|
TorControlCommand *currentCommand;
|
||||||
bool inDataReply;
|
bool inDataReply;
|
||||||
|
TorControlSocketClient *mClient;
|
||||||
|
|
||||||
void setError(const QString &message);
|
void setError(const std::string& message);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // TORCONTROLSOCKET_H
|
|
877
libretroshare/src/tor/TorManager.cpp
Normal file
877
libretroshare/src/tor/TorManager.cpp
Normal file
@ -0,0 +1,877 @@
|
|||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the names of the copyright owners nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// This works on linux only. I have no clue how to do that on windows. Anyway, this
|
||||||
|
// is only needed for an assert that should normaly never be triggered.
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "util/rsdir.h"
|
||||||
|
#include "retroshare/rsinit.h"
|
||||||
|
|
||||||
|
#include "TorManager.h"
|
||||||
|
#include "TorProcess.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
#include "HiddenService.h"
|
||||||
|
#include "GetConfCommand.h"
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
static TorManager *rsTor = nullptr;
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorManagerPrivate : public TorProcessClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TorManager *q;
|
||||||
|
TorProcess *process;
|
||||||
|
TorControl *control;
|
||||||
|
std::string dataDir;
|
||||||
|
std::string hiddenServiceDir;
|
||||||
|
std::list<std::string> logMessages;
|
||||||
|
std::string errorMessage;
|
||||||
|
bool configNeeded;
|
||||||
|
|
||||||
|
HiddenService *hiddenService ;
|
||||||
|
|
||||||
|
explicit TorManagerPrivate(TorManager *parent = 0);
|
||||||
|
virtual ~TorManagerPrivate();
|
||||||
|
|
||||||
|
std::string torExecutablePath() const;
|
||||||
|
bool createDataDir(const std::string &path);
|
||||||
|
bool createDefaultTorrc(const std::string &path);
|
||||||
|
|
||||||
|
void setError(const std::string &errorMessage);
|
||||||
|
|
||||||
|
virtual void processStateChanged(int state) override;
|
||||||
|
virtual void processErrorChanged(const std::string &errorMessage) override;
|
||||||
|
virtual void processLogMessage(const std::string &message) override;
|
||||||
|
|
||||||
|
//public slots:
|
||||||
|
void controlStatusChanged(int status);
|
||||||
|
void getConfFinished(TorControlCommand *sender);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManager::TorManager()
|
||||||
|
: d(new TorManagerPrivate(this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManager::~TorManager()
|
||||||
|
{
|
||||||
|
delete(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManagerPrivate::TorManagerPrivate(TorManager *parent)
|
||||||
|
: q(parent)
|
||||||
|
, process(0)
|
||||||
|
, control(new TorControl())
|
||||||
|
, configNeeded(false)
|
||||||
|
, hiddenService(NULL)
|
||||||
|
{
|
||||||
|
control->set_statusChanged_callback([this](int new_status,int /*old_status*/) { controlStatusChanged(new_status); });
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManagerPrivate::~TorManagerPrivate()
|
||||||
|
{
|
||||||
|
delete(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManager *TorManager::instance()
|
||||||
|
{
|
||||||
|
static TorManager *p = 0;
|
||||||
|
if (!p)
|
||||||
|
p = new TorManager();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControl *TorManager::control()
|
||||||
|
{
|
||||||
|
return d->control;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcess *TorManager::process()
|
||||||
|
{
|
||||||
|
return d->process;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorManager::torDataDirectory() const
|
||||||
|
{
|
||||||
|
return d->dataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::setTorDataDirectory(const std::string &path)
|
||||||
|
{
|
||||||
|
d->dataDir = path;
|
||||||
|
|
||||||
|
if (!d->dataDir.empty() && !ByteArray(d->dataDir).endsWith('/'))
|
||||||
|
d->dataDir += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorManager::hiddenServiceDirectory() const
|
||||||
|
{
|
||||||
|
return d->hiddenServiceDir;
|
||||||
|
}
|
||||||
|
void TorManager::setHiddenServiceDirectory(const std::string &path)
|
||||||
|
{
|
||||||
|
d->hiddenServiceDir = path;
|
||||||
|
|
||||||
|
if (!d->hiddenServiceDir.empty() && !(d->hiddenServiceDir.back() == '/'))
|
||||||
|
d->hiddenServiceDir += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_listening_port(const std::string& /*address*/,uint16_t port)
|
||||||
|
{
|
||||||
|
int sockfd;
|
||||||
|
struct sockaddr_in serv_addr ;
|
||||||
|
|
||||||
|
/* First call to socket() function */
|
||||||
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (sockfd < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Initialize socket structure */
|
||||||
|
memset((char *) &serv_addr, 0,sizeof(serv_addr));
|
||||||
|
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
/* Now bind the host address using bind() call.*/
|
||||||
|
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
|
||||||
|
perror("ERROR on binding");
|
||||||
|
close(sockfd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unix_fcntl_nonblock(sockfd);
|
||||||
|
int res = listen(sockfd,5);
|
||||||
|
|
||||||
|
close(sockfd);
|
||||||
|
|
||||||
|
if(!res)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool TorManager::setupHiddenService()
|
||||||
|
{
|
||||||
|
if(d->hiddenService != NULL)
|
||||||
|
{
|
||||||
|
RsErr() << "TorManager: setupHiddenService() called twice! Not doing anything this time." ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string keyData ;//= m_settings->read("serviceKey").toString();
|
||||||
|
std::string legacyDir = d->hiddenServiceDir;
|
||||||
|
|
||||||
|
RsDbg() << "TorManager: setting up hidden service." << std::endl;
|
||||||
|
|
||||||
|
if(legacyDir.empty())
|
||||||
|
{
|
||||||
|
RsErr() << "legacy dir not set! Cannot proceed." ;
|
||||||
|
return false ;
|
||||||
|
}
|
||||||
|
|
||||||
|
RsDbg() << "Using legacy dir: " << legacyDir ;
|
||||||
|
auto key_path = RsDirUtil::makePath(legacyDir,"/private_key");
|
||||||
|
|
||||||
|
if (!legacyDir.empty() && RsDirUtil::fileExists(key_path))
|
||||||
|
{
|
||||||
|
std::cerr << "Attempting to load key from legacy filesystem format from file \"" << key_path << "\"" << std::endl;
|
||||||
|
|
||||||
|
d->hiddenService = new Tor::HiddenService(this,legacyDir);
|
||||||
|
|
||||||
|
if(!d->hiddenService->privateKey().bytes().empty())
|
||||||
|
{
|
||||||
|
RsDbg() << "Got key from legacy dir: " ;
|
||||||
|
RsDbg() << d->hiddenService->privateKey().bytes().toHex().toString() ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
RsWarn() << "Failed to load existing hidden service. Creating a new one." ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d->hiddenService = new Tor::HiddenService(this,legacyDir);
|
||||||
|
|
||||||
|
RsDbg() << "Creating new hidden service." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(d->hiddenService);
|
||||||
|
|
||||||
|
// Generally, these are not used, and we bind to localhost and port 0
|
||||||
|
// for an automatic (and portable) selection.
|
||||||
|
|
||||||
|
std::string address = "127.0.0.1"; // we only listen from localhost
|
||||||
|
unsigned short hidden_service_port = 7934;//(quint16)m_settings->read("localListenPort").toInt();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
hidden_service_port = 1025 + (RsRandom::random_u32() >> 17);
|
||||||
|
|
||||||
|
std::cerr << "Testing listening address:port " << address << ":" << hidden_service_port ;
|
||||||
|
std::cerr.flush();
|
||||||
|
}
|
||||||
|
while(!test_listening_port(address,hidden_service_port));
|
||||||
|
|
||||||
|
std::cerr << ": OK - Adding hidden service to TorControl." << std::endl;
|
||||||
|
|
||||||
|
// Note: 9878 is quite arbitrary, but since each RS node generates its own hidden service, all of them
|
||||||
|
// can use the same port without any conflict.
|
||||||
|
|
||||||
|
d->hiddenService->addTarget(9878, "127.0.0.1",hidden_service_port);
|
||||||
|
control()->addHiddenService(d->hiddenService);
|
||||||
|
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::hiddenServiceStatusChanged(int new_status,int old_status)
|
||||||
|
{
|
||||||
|
std::cerr << "Hidden service status changed from " << old_status << " to " << new_status << std::endl;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::TOR_STATUS_CHANGED;
|
||||||
|
ev->mTorConnectivityStatus = RsTorConnectivityStatus::HIDDEN_SERVICE_READY;
|
||||||
|
ev->mTorStatus = RsTorStatus::READY;
|
||||||
|
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::hiddenServicePrivateKeyChanged()
|
||||||
|
{
|
||||||
|
if(!d->hiddenService)
|
||||||
|
return ;
|
||||||
|
|
||||||
|
std::string key = d->hiddenService->privateKey().bytes().toString();
|
||||||
|
|
||||||
|
std::ofstream s(d->hiddenServiceDir + "/private_key");
|
||||||
|
|
||||||
|
#ifdef TO_REMOVE
|
||||||
|
s << "-----BEGIN RSA PRIVATE KEY-----" << endl;
|
||||||
|
|
||||||
|
for(int i=0;i<key.length();i+=64)
|
||||||
|
s << key.mid(i,64) << endl ;
|
||||||
|
|
||||||
|
s << "-----END RSA PRIVATE KEY-----" << endl;
|
||||||
|
#endif
|
||||||
|
s << key ;
|
||||||
|
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
std::cerr << "Hidden service private key changed!" << std::endl;
|
||||||
|
std::cerr << key << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::hiddenServiceHostnameChanged()
|
||||||
|
{
|
||||||
|
if(!d->hiddenService)
|
||||||
|
return ;
|
||||||
|
|
||||||
|
std::string outfile2_name = RsDirUtil::makePath(d->hiddenServiceDir,"/hostname") ;
|
||||||
|
std::ofstream of(outfile2_name);
|
||||||
|
|
||||||
|
std::string hostname(d->hiddenService->hostname());
|
||||||
|
|
||||||
|
of << hostname << std::endl;
|
||||||
|
of.close();
|
||||||
|
|
||||||
|
std::cerr << "Hidden service hostname changed: " << hostname << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::configurationNeeded() const
|
||||||
|
{
|
||||||
|
return d->configNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::list<std::string>& TorManager::logMessages() const
|
||||||
|
{
|
||||||
|
return d->logMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::hasError() const
|
||||||
|
{
|
||||||
|
return !d->errorMessage.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorManager::errorMessage() const
|
||||||
|
{
|
||||||
|
return d->errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::startTorManager()
|
||||||
|
{
|
||||||
|
if (!d->errorMessage.empty()) {
|
||||||
|
d->errorMessage.clear();
|
||||||
|
|
||||||
|
//emit errorChanged(); // not needed because there's no error to handle
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
SettingsObject settings("tor");
|
||||||
|
|
||||||
|
// If a control port is defined by config or environment, skip launching tor
|
||||||
|
if (!settings.read("controlPort").isUndefined() ||
|
||||||
|
!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT"))
|
||||||
|
{
|
||||||
|
QHostAddress address(settings.read("controlAddress").toString());
|
||||||
|
quint16 port = (quint16)settings.read("controlPort").toInt();
|
||||||
|
QByteArray password = settings.read("controlPassword").toString().toLatin1();
|
||||||
|
|
||||||
|
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_HOST"))
|
||||||
|
address = QHostAddress(qgetenv("TOR_CONTROL_HOST"));
|
||||||
|
|
||||||
|
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) {
|
||||||
|
bool ok = false;
|
||||||
|
port = qgetenv("TOR_CONTROL_PORT").toUShort(&ok);
|
||||||
|
if (!ok)
|
||||||
|
port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PASSWD"))
|
||||||
|
password = qgetenv("TOR_CONTROL_PASSWD");
|
||||||
|
|
||||||
|
if (!port) {
|
||||||
|
d->setError("Invalid control port settings from environment or configuration");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.isNull())
|
||||||
|
address = QHostAddress::LocalHost;
|
||||||
|
|
||||||
|
d->control->setAuthPassword(password);
|
||||||
|
d->control->connect(address, port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// Launch a bundled Tor instance
|
||||||
|
std::string executable = d->torExecutablePath();
|
||||||
|
|
||||||
|
std::cerr << "Executable path: " << executable << std::endl;
|
||||||
|
|
||||||
|
if (executable.empty()) {
|
||||||
|
d->setError("Cannot find tor executable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d->process) {
|
||||||
|
d->process = new TorProcess(d);
|
||||||
|
|
||||||
|
// QObject::connect(d->process, SIGNAL(stateChanged(int)), d, SLOT(processStateChanged(int)));
|
||||||
|
// QObject::connect(d->process, SIGNAL(errorMessageChanged(std::string)), d, SLOT(processErrorChanged(std::string)));
|
||||||
|
// QObject::connect(d->process, SIGNAL(logMessage(std::string)), d, SLOT(processLogMessage(std::string)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RsDirUtil::checkCreateDirectory(d->dataDir))
|
||||||
|
{
|
||||||
|
d->setError(std::string("Cannot write data location: ") + d->dataDir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string defaultTorrc = RsDirUtil::makePath(d->dataDir,"default_torrc");
|
||||||
|
|
||||||
|
if (!RsDirUtil::fileExists(defaultTorrc) && !d->createDefaultTorrc(defaultTorrc))
|
||||||
|
{
|
||||||
|
d->setError("Cannot write data files: "+defaultTorrc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string torrc = RsDirUtil::makePath(d->dataDir,"torrc");
|
||||||
|
uint64_t file_size;
|
||||||
|
|
||||||
|
bool torrc_exists = RsDirUtil::checkFile(torrc,file_size);
|
||||||
|
|
||||||
|
if(!torrc_exists || torrc.size() == 0)
|
||||||
|
{
|
||||||
|
d->configNeeded = true;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::CONFIGURATION_NEEDED;
|
||||||
|
ev->mTorConnectivityStatus = RsTorConnectivityStatus::UNKNOWN;
|
||||||
|
ev->mTorStatus = RsTorStatus::UNKNOWN;
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
//emit configurationNeededChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Starting Tor process:" << std::endl;
|
||||||
|
std::cerr << " Tor executable path: " << executable << std::endl;
|
||||||
|
std::cerr << " Tor data directory : " << d->dataDir << std::endl;
|
||||||
|
std::cerr << " Tor default torrc : " << defaultTorrc << std::endl;
|
||||||
|
|
||||||
|
d->process->setExecutable(executable);
|
||||||
|
d->process->setDataDir(d->dataDir);
|
||||||
|
d->process->setDefaultTorrc(defaultTorrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Starting Tor manager thread:" << std::endl;
|
||||||
|
RsThread::start("TorManager");
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::run()
|
||||||
|
{
|
||||||
|
d->process->start();
|
||||||
|
|
||||||
|
while(!shouldStop())
|
||||||
|
{
|
||||||
|
threadTick();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
d->control->shutdownSync();
|
||||||
|
d->process->stop();
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::TOR_MANAGER_STOPPED;
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManager::threadTick()
|
||||||
|
{
|
||||||
|
d->process->tick();
|
||||||
|
|
||||||
|
if(d->process->state() != TorProcess::Ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(d->control->status())
|
||||||
|
{
|
||||||
|
case TorControl::Unknown:
|
||||||
|
case TorControl::Connecting:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TorControl::NotConnected:
|
||||||
|
RsDbg() << "Connecting to tor process at " << d->process->controlHost() << ":" << d->process->controlPort() << "..." ;
|
||||||
|
d->control->connect(d->process->controlHost(),d->process->controlPort());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TorControl::SocketConnected:
|
||||||
|
RsDbg() << "Connection established." ;
|
||||||
|
|
||||||
|
if(d->hiddenService == nullptr)
|
||||||
|
{
|
||||||
|
RsDbg() << "Setting up hidden service" ;
|
||||||
|
setupHiddenService();
|
||||||
|
}
|
||||||
|
|
||||||
|
d->control->setAuthPassword(d->process->controlPassword());
|
||||||
|
d->control->authenticate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TorControl::Authenticating:
|
||||||
|
RsDbg() << "Authenticating..." ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TorControl::Authenticated:
|
||||||
|
|
||||||
|
RsDbg() << "Authenticated. Looking for hidden services.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TorControl::HiddenServiceReady:
|
||||||
|
if(d->control->torStatus() < TorControl::TorReady)
|
||||||
|
{
|
||||||
|
d->control->getTorInfo(); // forces TorControl to check its state.
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TorControl::Error:
|
||||||
|
d->control->shutdown();
|
||||||
|
d->control->reconnect();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::getProxyServerInfo(std::string& proxy_server_adress,uint16_t& proxy_server_port)
|
||||||
|
{
|
||||||
|
proxy_server_adress = control()->socksAddress();
|
||||||
|
proxy_server_port = control()->socksPort();
|
||||||
|
|
||||||
|
return proxy_server_port > 1023 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManager::getHiddenServiceInfo(std::string& service_id,std::string& service_onion_address,uint16_t& service_port, std::string& service_target_address,uint16_t& target_port)
|
||||||
|
{
|
||||||
|
auto hidden_services = control()->hiddenServices();
|
||||||
|
|
||||||
|
if(hidden_services.empty())
|
||||||
|
return false ;
|
||||||
|
|
||||||
|
// Only return the first one.
|
||||||
|
|
||||||
|
for(auto it(hidden_services.begin());it!=hidden_services.end();++it)
|
||||||
|
{
|
||||||
|
service_onion_address = (*it)->hostname();
|
||||||
|
service_id = (*it)->serviceId();
|
||||||
|
|
||||||
|
for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2)
|
||||||
|
{
|
||||||
|
service_port = (*it2).servicePort ;
|
||||||
|
service_target_address = (*it2).targetAddress ;
|
||||||
|
target_port = (*it2).targetPort;
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::processStateChanged(int state)
|
||||||
|
{
|
||||||
|
RsInfo() << "state: " << state << " passwd=\"" << process->controlPassword().toString() << "\" " << process->controlHost()
|
||||||
|
<< ":" << process->controlPort() << std::endl;
|
||||||
|
|
||||||
|
if (state == TorProcess::Ready) {
|
||||||
|
control->setAuthPassword(process->controlPassword());
|
||||||
|
control->connect(process->controlHost(), process->controlPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::processErrorChanged(const std::string &errorMessage)
|
||||||
|
{
|
||||||
|
std::cerr << "tor error:" << errorMessage << std::endl;
|
||||||
|
setError(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::processLogMessage(const std::string &message)
|
||||||
|
{
|
||||||
|
std::cerr << "tor:" << message << std::endl;
|
||||||
|
if (logMessages.size() >= 50)
|
||||||
|
logMessages.pop_front();
|
||||||
|
|
||||||
|
auto p = message.find_first_of('\n');
|
||||||
|
|
||||||
|
logMessages.push_back((p==std::string::npos)?message:message.substr(0,p));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::controlStatusChanged(int status)
|
||||||
|
{
|
||||||
|
if (status == TorControl::Authenticated) {
|
||||||
|
if (!configNeeded) {
|
||||||
|
// If DisableNetwork is 1, trigger configurationNeeded
|
||||||
|
auto cmd = control->getConfiguration("DisableNetwork");
|
||||||
|
cmd->set_finished_callback( [this](TorControlCommand *sender) { getConfFinished(sender) ; });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process) {
|
||||||
|
// Take ownership via this control socket
|
||||||
|
control->takeOwnership();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::getConfFinished(TorControlCommand *sender)
|
||||||
|
{
|
||||||
|
GetConfCommand *command = dynamic_cast<GetConfCommand*>(sender);
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for(auto str:command->get("DisableNetwork"))
|
||||||
|
if(RsUtil::StringToInt(str,n) && n==1 && !configNeeded)
|
||||||
|
{
|
||||||
|
configNeeded = true;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::CONFIGURATION_NEEDED;
|
||||||
|
ev->mTorConnectivityStatus = RsTorConnectivityStatus::UNKNOWN;
|
||||||
|
ev->mTorStatus = RsTorStatus::UNKNOWN;
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorManagerPrivate::torExecutablePath() const
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
#ifdef TODO
|
||||||
|
SettingsObject settings("tor");
|
||||||
|
path = settings.read("executablePath").toString();
|
||||||
|
|
||||||
|
if (!path.isEmpty() && QFile::exists(path))
|
||||||
|
return path;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
std::string filename("/tor/tor.exe");
|
||||||
|
#else
|
||||||
|
std::string filename("/tor");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
path = RsDirUtil::getDirectory(RsInit::executablePath());
|
||||||
|
std::string tor_exe_path = RsDirUtil::makePath(path,filename);
|
||||||
|
|
||||||
|
if (RsDirUtil::fileExists(tor_exe_path))
|
||||||
|
return tor_exe_path;
|
||||||
|
|
||||||
|
#ifdef BUNDLED_TOR_PATH
|
||||||
|
path = BUNDLED_TOR_PATH;
|
||||||
|
tor_exe_path = RsDirUtil::makePath(path,filename);
|
||||||
|
|
||||||
|
if (RsDirUtil::fileExists(tor_exe_path))
|
||||||
|
return tor_exe_path;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// on MacOS, try traditional brew installation path
|
||||||
|
|
||||||
|
path = "/usr/local/opt/tor/bin" ;
|
||||||
|
tor_exe_path = RsDirUtil::makePath(path,filename);
|
||||||
|
|
||||||
|
if (RsDirUtil::fileExists(tor_exe_path))
|
||||||
|
return tor_exe_path;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
// On linux try system-installed tor /usr/bin/tor
|
||||||
|
|
||||||
|
if(RsDirUtil::fileExists("/usr/bin/tor"))
|
||||||
|
return std::string("/usr/bin/tor");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RsErr() << "Could not find Tor executable anywhere!" ;
|
||||||
|
// Try $PATH
|
||||||
|
return filename.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManagerPrivate::createDataDir(const std::string &path)
|
||||||
|
{
|
||||||
|
return RsDirUtil::checkCreateDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorManagerPrivate::createDefaultTorrc(const std::string &path)
|
||||||
|
{
|
||||||
|
static const char defaultTorrcContent[] =
|
||||||
|
"SocksPort auto\n"
|
||||||
|
"AvoidDiskWrites 1\n"
|
||||||
|
// "DisableNetwork 1\n" // (cyril) I removed this because it prevents Tor to bootstrap.
|
||||||
|
"__ReloadTorrcOnSIGHUP 0\n";
|
||||||
|
|
||||||
|
FILE *f = fopen(path.c_str(),"w");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fprintf(f,"%s",defaultTorrcContent);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorManagerPrivate::setError(const std::string &message)
|
||||||
|
{
|
||||||
|
errorMessage = message;
|
||||||
|
|
||||||
|
if(rsEvents)
|
||||||
|
{
|
||||||
|
auto ev = std::make_shared<RsTorManagerEvent>();
|
||||||
|
|
||||||
|
ev->mTorManagerEventType = RsTorManagerEventCode::TOR_MANAGER_ERROR;
|
||||||
|
ev->mErrorMessage = message;
|
||||||
|
rsEvents->sendEvent(ev);
|
||||||
|
}
|
||||||
|
//emit q->errorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsTor::isTorAvailable()
|
||||||
|
{
|
||||||
|
return !instance()->d->torExecutablePath().empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsTor::getHiddenServiceInfo(std::string& service_id,
|
||||||
|
std::string& service_onion_address,
|
||||||
|
uint16_t& service_port,
|
||||||
|
std::string& service_target_address,
|
||||||
|
uint16_t& target_port)
|
||||||
|
{
|
||||||
|
std::string sid;
|
||||||
|
std::string soa;
|
||||||
|
std::string sta;
|
||||||
|
|
||||||
|
if(!instance()->getHiddenServiceInfo(sid,soa,service_port,sta,target_port))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
service_id = sid;
|
||||||
|
service_onion_address = soa;
|
||||||
|
service_target_address = sta;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<std::string> RsTor::logMessages()
|
||||||
|
{
|
||||||
|
return instance()->logMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RsTor::socksAddress()
|
||||||
|
{
|
||||||
|
return instance()->control()->socksAddress();
|
||||||
|
}
|
||||||
|
uint16_t RsTor::socksPort()
|
||||||
|
{
|
||||||
|
return instance()->control()->socksPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
RsTorStatus RsTor::torStatus()
|
||||||
|
{
|
||||||
|
TorControl::TorStatus ts = instance()->control()->torStatus();
|
||||||
|
|
||||||
|
switch(ts)
|
||||||
|
{
|
||||||
|
case TorControl::TorOffline: return RsTorStatus::OFFLINE;
|
||||||
|
case TorControl::TorReady: return RsTorStatus::READY;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case TorControl::TorUnknown: return RsTorStatus::UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RsTorConnectivityStatus RsTor::torConnectivityStatus()
|
||||||
|
{
|
||||||
|
TorControl::Status ts = instance()->control()->status();
|
||||||
|
|
||||||
|
switch(ts)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Tor::TorControl::Error : return RsTorConnectivityStatus::ERROR;
|
||||||
|
case Tor::TorControl::NotConnected : return RsTorConnectivityStatus::NOT_CONNECTED;
|
||||||
|
case Tor::TorControl::Authenticating: return RsTorConnectivityStatus::AUTHENTICATING;
|
||||||
|
case Tor::TorControl::Connecting: return RsTorConnectivityStatus::CONNECTING;
|
||||||
|
case Tor::TorControl::Authenticated : return RsTorConnectivityStatus::AUTHENTICATED;
|
||||||
|
case Tor::TorControl::HiddenServiceReady : return RsTorConnectivityStatus::HIDDEN_SERVICE_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsTor::setupHiddenService()
|
||||||
|
{
|
||||||
|
return instance()->setupHiddenService();
|
||||||
|
}
|
||||||
|
|
||||||
|
RsTorHiddenServiceStatus RsTor::getHiddenServiceStatus(std::string& service_id)
|
||||||
|
{
|
||||||
|
service_id.clear();
|
||||||
|
auto list = instance()->control()->hiddenServices();
|
||||||
|
|
||||||
|
if(list.empty())
|
||||||
|
return RsTorHiddenServiceStatus::NOT_CREATED;
|
||||||
|
|
||||||
|
service_id = (*list.begin())->serviceId();
|
||||||
|
|
||||||
|
switch((*list.begin())->status())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Tor::HiddenService::NotCreated: return RsTorHiddenServiceStatus::NOT_CREATED;
|
||||||
|
case Tor::HiddenService::Offline : return RsTorHiddenServiceStatus::OFFLINE;
|
||||||
|
case Tor::HiddenService::Online : return RsTorHiddenServiceStatus::ONLINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string,std::string> RsTor::bootstrapStatus()
|
||||||
|
{
|
||||||
|
return instance()->control()->bootstrapStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsTor::hasError()
|
||||||
|
{
|
||||||
|
return instance()->hasError();
|
||||||
|
}
|
||||||
|
std::string RsTor::errorMessage()
|
||||||
|
{
|
||||||
|
return instance()->errorMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsTor::getProxyServerInfo(std::string& server_address, uint16_t& server_port)
|
||||||
|
{
|
||||||
|
std::string qserver_address;
|
||||||
|
instance()->getProxyServerInfo(qserver_address,server_port);
|
||||||
|
|
||||||
|
server_address = qserver_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsTor::start()
|
||||||
|
{
|
||||||
|
return instance()->startTorManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsTor::stop()
|
||||||
|
{
|
||||||
|
if (rsTor) {
|
||||||
|
if (rsTor->isRunning()) {
|
||||||
|
rsTor->fullstop();
|
||||||
|
}
|
||||||
|
delete(rsTor);
|
||||||
|
rsTor= nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsTor::setTorDataDirectory(const std::string& dir)
|
||||||
|
{
|
||||||
|
instance()->setTorDataDirectory(dir);
|
||||||
|
}
|
||||||
|
void RsTor::setHiddenServiceDirectory(const std::string& dir)
|
||||||
|
{
|
||||||
|
instance()->setHiddenServiceDirectory(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
TorManager *RsTor::instance()
|
||||||
|
{
|
||||||
|
#if !defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
assert(getpid() == syscall(SYS_gettid));// make sure we're not in a thread
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(rsTor == nullptr)
|
||||||
|
rsTor = new TorManager;
|
||||||
|
|
||||||
|
return rsTor;
|
||||||
|
}
|
@ -32,12 +32,10 @@
|
|||||||
|
|
||||||
// This code has been further modified to fit Retroshare context.
|
// This code has been further modified to fit Retroshare context.
|
||||||
|
|
||||||
#ifndef TORMANAGER_H
|
#pragma once
|
||||||
#define TORMANAGER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include "retroshare/rstor.h"
|
||||||
#include <QStringList>
|
#include "HiddenService.h"
|
||||||
#include <QHostAddress>
|
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
@ -48,31 +46,21 @@ class TorManagerPrivate;
|
|||||||
|
|
||||||
/* Run/connect to an instance of Tor according to configuration, and manage
|
/* Run/connect to an instance of Tor according to configuration, and manage
|
||||||
* UI interaction, first time configuration, etc. */
|
* UI interaction, first time configuration, etc. */
|
||||||
class TorManager : public QObject
|
|
||||||
|
class TorManager : public HiddenServiceClient, public RsThread, public RsTor
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(bool configurationNeeded READ configurationNeeded NOTIFY configurationNeededChanged)
|
|
||||||
Q_PROPERTY(QStringList logMessages READ logMessages CONSTANT)
|
|
||||||
Q_PROPERTY(Tor::TorProcess* process READ process CONSTANT)
|
|
||||||
Q_PROPERTY(Tor::TorControl* control READ control CONSTANT)
|
|
||||||
Q_PROPERTY(bool hasError READ hasError NOTIFY errorChanged)
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorChanged)
|
|
||||||
Q_PROPERTY(QString torDataDirectory READ torDataDirectory WRITE setTorDataDirectory)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool isTorAvailable() ;
|
|
||||||
static TorManager *instance();
|
static TorManager *instance();
|
||||||
|
virtual ~TorManager();
|
||||||
|
|
||||||
TorProcess *process();
|
TorProcess *process();
|
||||||
TorControl *control();
|
TorControl *control();
|
||||||
|
|
||||||
|
std::string torDataDirectory() const;
|
||||||
|
void setTorDataDirectory(const std::string &path);
|
||||||
|
|
||||||
QString torDataDirectory() const;
|
std::string hiddenServiceDirectory() const;
|
||||||
void setTorDataDirectory(const QString &path);
|
void setHiddenServiceDirectory(const std::string &path);
|
||||||
|
|
||||||
QString hiddenServiceDirectory() const;
|
|
||||||
void setHiddenServiceDirectory(const QString &path);
|
|
||||||
|
|
||||||
// Starts a hidden service, loading it from the config directory that has been set earlier.
|
// Starts a hidden service, loading it from the config directory that has been set earlier.
|
||||||
bool setupHiddenService() ;
|
bool setupHiddenService() ;
|
||||||
@ -80,32 +68,30 @@ public:
|
|||||||
// True on first run or when the Tor configuration wizard needs to be shown
|
// True on first run or when the Tor configuration wizard needs to be shown
|
||||||
bool configurationNeeded() const;
|
bool configurationNeeded() const;
|
||||||
|
|
||||||
QStringList logMessages() const;
|
const std::list<std::string>& logMessages() const;
|
||||||
|
|
||||||
bool hasError() const;
|
bool hasError() const;
|
||||||
QString errorMessage() const;
|
std::string errorMessage() const;
|
||||||
|
|
||||||
bool getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port);
|
bool getHiddenServiceInfo(std::string& service_id,std::string& service_onion_address,uint16_t& service_port, std::string& service_target_address,uint16_t& target_port);
|
||||||
bool getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port);
|
bool getProxyServerInfo(std::string &proxy_server_adress, uint16_t& proxy_server_port);
|
||||||
|
|
||||||
public slots:
|
bool startTorManager();
|
||||||
bool start();
|
|
||||||
|
|
||||||
private slots:
|
virtual void hiddenServiceOnline() override {} // do nothing here.
|
||||||
void hiddenServicePrivateKeyChanged();
|
virtual void hiddenServicePrivateKeyChanged() override;
|
||||||
void hiddenServiceHostnameChanged();
|
virtual void hiddenServiceHostnameChanged() override;
|
||||||
void hiddenServiceStatusChanged(int old_status,int new_status);
|
virtual void hiddenServiceStatusChanged(int new_status, int old_status) override;
|
||||||
|
|
||||||
signals:
|
// Thread stuff
|
||||||
void configurationNeededChanged();
|
|
||||||
void errorChanged();
|
virtual void run() override;
|
||||||
|
void threadTick() ;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit TorManager(QObject *parent = 0);
|
explicit TorManager();
|
||||||
TorManagerPrivate *d;
|
TorManagerPrivate *d;
|
||||||
|
friend class RsTor;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
#
|
|
510
libretroshare/src/tor/TorProcess.cpp
Normal file
510
libretroshare/src/tor/TorProcess.cpp
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the names of the copyright owners nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "util/rsdir.h"
|
||||||
|
#include "util/rsfile.h"
|
||||||
|
#include "pqi/pqifdbin.h"
|
||||||
|
|
||||||
|
#include "TorProcess.h"
|
||||||
|
#include "CryptoKey.h"
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
#include "util/rsstring.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#define pipe(fds) _pipe(fds, 1024, _O_BINARY)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace Tor;
|
||||||
|
|
||||||
|
static const int INTERVAL_BETWEEN_CONTROL_PORT_READ_TRIES = 5; // try every 5 secs.
|
||||||
|
|
||||||
|
TorProcess::TorProcess(TorProcessClient *client)
|
||||||
|
: m_client(client), mState(TorProcess::NotStarted), mControlPort(0), mLastTryReadControlPort(0)
|
||||||
|
{
|
||||||
|
mControlPortReadNbTries=0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcess::~TorProcess()
|
||||||
|
{
|
||||||
|
if (state() > NotStarted)
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::executable() const
|
||||||
|
{
|
||||||
|
return mExecutable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setExecutable(const std::string &path)
|
||||||
|
{
|
||||||
|
mExecutable = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::dataDir() const
|
||||||
|
{
|
||||||
|
return mDataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setDataDir(const std::string &path)
|
||||||
|
{
|
||||||
|
mDataDir = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::defaultTorrc() const
|
||||||
|
{
|
||||||
|
return mDefaultTorrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setDefaultTorrc(const std::string &path)
|
||||||
|
{
|
||||||
|
mDefaultTorrc = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<std::string> TorProcess::extraSettings() const
|
||||||
|
{
|
||||||
|
return mExtraSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::setExtraSettings(const std::list<std::string> &settings)
|
||||||
|
{
|
||||||
|
mExtraSettings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorProcess::State TorProcess::state() const
|
||||||
|
{
|
||||||
|
return mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::errorMessage() const
|
||||||
|
{
|
||||||
|
return mErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does a popen, but dup all file descriptors (STDIN STDOUT and STDERR) to the
|
||||||
|
// FDs supplied by the parent process
|
||||||
|
|
||||||
|
int popen3(int fd[3],const std::vector<std::string>& args,TorProcessHandle& pid)
|
||||||
|
{
|
||||||
|
RsErr() << "Launching Tor in background..." ;
|
||||||
|
|
||||||
|
int i, e;
|
||||||
|
int p[3][2];
|
||||||
|
// set all the FDs to invalid
|
||||||
|
for(i=0; i<3; i++)
|
||||||
|
p[i][0] = p[i][1] = -1;
|
||||||
|
// create the pipes
|
||||||
|
for(int i=0; i<3; i++)
|
||||||
|
if(pipe(p[i]))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
// Set up members of the PROCESS_INFORMATION structure.
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
||||||
|
|
||||||
|
// Set up members of the STARTUPINFO structure.
|
||||||
|
// This structure specifies the STDIN and STDOUT handles for redirection.
|
||||||
|
STARTUPINFO si;
|
||||||
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||||
|
si.cb = sizeof(STARTUPINFO);
|
||||||
|
si.hStdInput = (HANDLE) _get_osfhandle(p[STDIN_FILENO][0]);
|
||||||
|
si.hStdOutput = (HANDLE) _get_osfhandle(p[STDOUT_FILENO][1]);
|
||||||
|
si.hStdError = (HANDLE) _get_osfhandle(p[STDERR_FILENO][1]);
|
||||||
|
si.wShowWindow = SW_HIDE;
|
||||||
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
|
|
||||||
|
if (si.hStdInput != INVALID_HANDLE_VALUE &&
|
||||||
|
si.hStdOutput != INVALID_HANDLE_VALUE &&
|
||||||
|
si.hStdError != INVALID_HANDLE_VALUE) {
|
||||||
|
// build commandline
|
||||||
|
std::string cmd;
|
||||||
|
for (std::vector<std::string>::const_iterator it = args.begin(); it != args.end(); ++it) {
|
||||||
|
if (it != args.begin()) {
|
||||||
|
cmd += " ";
|
||||||
|
}
|
||||||
|
cmd += *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring wcmd;
|
||||||
|
if (!librs::util::ConvertUtf8ToUtf16(cmd, wcmd)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
WINBOOL success = CreateProcess(nullptr,
|
||||||
|
(LPWSTR) wcmd.c_str(), // command line
|
||||||
|
nullptr, // process security attributes
|
||||||
|
nullptr, // primary thread security attributes
|
||||||
|
TRUE, // handles are inherited
|
||||||
|
0, // creation flags
|
||||||
|
nullptr, // use parent's environment
|
||||||
|
nullptr, // use parent's current directory
|
||||||
|
&si, // STARTUPINFO pointer
|
||||||
|
&pi); // receives PROCESS_INFORMATION
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
pid = pi.hProcess;
|
||||||
|
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
|
fd[STDIN_FILENO] = p[STDIN_FILENO][1];
|
||||||
|
close(p[STDIN_FILENO][0]);
|
||||||
|
fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
|
||||||
|
close(p[STDOUT_FILENO][1]);
|
||||||
|
fd[STDERR_FILENO] = p[STDERR_FILENO][0];
|
||||||
|
close(p[STDERR_FILENO][1]);
|
||||||
|
|
||||||
|
// success
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall through error
|
||||||
|
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
const char *arguments[args.size()+1];
|
||||||
|
int n=0;
|
||||||
|
|
||||||
|
// We first pushed everything into a vector of strings to save the pointers obtained from string returning methods
|
||||||
|
// by the time the process is launched.
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<args.size();++i)
|
||||||
|
arguments[n++]= args[i].data();
|
||||||
|
|
||||||
|
arguments[n] = nullptr;
|
||||||
|
|
||||||
|
// and fork
|
||||||
|
pid = fork();
|
||||||
|
if(-1 == pid)
|
||||||
|
goto error;
|
||||||
|
// in the parent?
|
||||||
|
if(pid)
|
||||||
|
{
|
||||||
|
// parent
|
||||||
|
fd[STDIN_FILENO] = p[STDIN_FILENO][1];
|
||||||
|
close(p[STDIN_FILENO][0]);
|
||||||
|
fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
|
||||||
|
close(p[STDOUT_FILENO][1]);
|
||||||
|
fd[STDERR_FILENO] = p[STDERR_FILENO][0];
|
||||||
|
close(p[STDERR_FILENO][1]);
|
||||||
|
// success
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RsErr() << "Launching sub-process..." ;
|
||||||
|
// child
|
||||||
|
dup2(p[STDIN_FILENO][0],STDIN_FILENO);
|
||||||
|
close(p[STDIN_FILENO][1]);
|
||||||
|
dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
|
||||||
|
close(p[STDOUT_FILENO][0]);
|
||||||
|
dup2(p[STDERR_FILENO][1],STDERR_FILENO);
|
||||||
|
close(p[STDERR_FILENO][0]);
|
||||||
|
|
||||||
|
// here we try and run it
|
||||||
|
|
||||||
|
execv(*arguments,const_cast<char*const*>(arguments));
|
||||||
|
|
||||||
|
// if we are there, then we failed to launch our program
|
||||||
|
perror("Could not launch");
|
||||||
|
fprintf(stderr," \"%s\"\n",*arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error:
|
||||||
|
e = errno;
|
||||||
|
// preserve original error
|
||||||
|
RsErr() << "An error occurred while trying to launch tor in background." ;
|
||||||
|
for(i=0; i<3; i++) {
|
||||||
|
close(p[i][0]);
|
||||||
|
close(p[i][1]);
|
||||||
|
}
|
||||||
|
errno = e;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::start()
|
||||||
|
{
|
||||||
|
if (state() > NotStarted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mErrorMessage.clear();
|
||||||
|
|
||||||
|
if (mExecutable.empty() || mDataDir.empty()) {
|
||||||
|
mErrorMessage = "Tor executable and data directory not specified";
|
||||||
|
mState = Failed;
|
||||||
|
|
||||||
|
if(m_client) m_client->processStateChanged(mState); // emit stateChanged(d->state);
|
||||||
|
if(m_client) m_client->processErrorChanged(mErrorMessage); // emit errorMessageChanged(d->errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ensureFilesExist()) {
|
||||||
|
mState = Failed;
|
||||||
|
if(m_client) m_client->processErrorChanged(mErrorMessage);// emit errorMessageChanged(d->errorMessage);
|
||||||
|
if(m_client) m_client->processStateChanged(mState);// emit stateChanged(d->state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray password = controlPassword();
|
||||||
|
ByteArray hashedPassword = torControlHashedPassword(password);
|
||||||
|
|
||||||
|
if (password.empty() || hashedPassword.empty()) {
|
||||||
|
mErrorMessage = "Random password generation failed";
|
||||||
|
mState = Failed;
|
||||||
|
if(m_client) m_client->processErrorChanged(mErrorMessage);// emit errorMessageChanged(d->errorMessage);
|
||||||
|
if(m_client) m_client->processStateChanged(mState); // emit stateChanged(d->state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
RsDbg() << "Using ControlPasswd=\"" << password.toString() << "\", hashed version=\"" << hashedPassword.toString() << "\"" ;
|
||||||
|
|
||||||
|
mState = Starting;
|
||||||
|
|
||||||
|
if(m_client) m_client->processStateChanged(mState);// emit stateChanged(d->state);
|
||||||
|
|
||||||
|
if (RsDirUtil::fileExists(controlPortFilePath()))
|
||||||
|
RsDirUtil::removeFile(controlPortFilePath());
|
||||||
|
|
||||||
|
mControlPort = 0;
|
||||||
|
mControlHost.clear();
|
||||||
|
|
||||||
|
// Launch the process
|
||||||
|
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
args.push_back(mExecutable);
|
||||||
|
|
||||||
|
if (!mDefaultTorrc.empty())
|
||||||
|
{
|
||||||
|
args.push_back("--defaults-torrc");
|
||||||
|
args.push_back(mDefaultTorrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push_back("-f");
|
||||||
|
args.push_back(torrcPath());
|
||||||
|
|
||||||
|
args.push_back("DataDirectory") ;
|
||||||
|
args.push_back(mDataDir);
|
||||||
|
|
||||||
|
args.push_back("HashedControlPassword") ;
|
||||||
|
args.push_back(hashedPassword.toString());
|
||||||
|
|
||||||
|
args.push_back("ControlPort") ;
|
||||||
|
args.push_back("auto");
|
||||||
|
|
||||||
|
args.push_back("ControlPortWriteToFile");
|
||||||
|
args.push_back(controlPortFilePath());
|
||||||
|
|
||||||
|
args.push_back("__OwningControllerProcess") ;
|
||||||
|
args.push_back(RsUtil::NumberToString(getpid()));
|
||||||
|
|
||||||
|
for(auto s:mExtraSettings)
|
||||||
|
args.push_back(s);
|
||||||
|
|
||||||
|
int fd[3]; // File descriptors array
|
||||||
|
|
||||||
|
if(popen3(fd,args,mTorProcessId))
|
||||||
|
{
|
||||||
|
RsErr() << "Could not start Tor process. errno=" << errno ;
|
||||||
|
mState = Failed;
|
||||||
|
return; // stop the control thread
|
||||||
|
}
|
||||||
|
|
||||||
|
RsFileUtil::set_fd_nonblock(fd[STDOUT_FILENO]);
|
||||||
|
RsFileUtil::set_fd_nonblock(fd[STDERR_FILENO]);
|
||||||
|
|
||||||
|
mStdOutFD = new RsFdBinInterface(fd[STDOUT_FILENO], false);
|
||||||
|
mStdErrFD = new RsFdBinInterface(fd[STDERR_FILENO], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::tick()
|
||||||
|
{
|
||||||
|
mStdOutFD->tick();
|
||||||
|
mStdErrFD->tick();
|
||||||
|
|
||||||
|
unsigned char buff[1024];
|
||||||
|
int s;
|
||||||
|
|
||||||
|
if((s=mStdOutFD->readline(buff,1024))) logMessage(std::string((char*)buff,s));
|
||||||
|
if((s=mStdErrFD->readline(buff,1024))) logMessage(std::string((char*)buff,s));
|
||||||
|
|
||||||
|
if(!mStdOutFD->isactive() && !mStdErrFD->isactive())
|
||||||
|
{
|
||||||
|
RsErr() << "Tor process died. Exiting TorControl process." ;
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
|
if(mControlPortReadNbTries <= 10 && (mControlPort==0 || mControlHost.empty()) && mLastTryReadControlPort + INTERVAL_BETWEEN_CONTROL_PORT_READ_TRIES < now)
|
||||||
|
{
|
||||||
|
mLastTryReadControlPort = now;
|
||||||
|
|
||||||
|
if(tryReadControlPort())
|
||||||
|
{
|
||||||
|
mState = Ready;
|
||||||
|
m_client->processStateChanged(mState);// stateChanged(mState);
|
||||||
|
}
|
||||||
|
else if(mControlPortReadNbTries > 10)
|
||||||
|
{
|
||||||
|
mState = Failed;
|
||||||
|
m_client->processStateChanged(mState);// stateChanged(mState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::stop()
|
||||||
|
{
|
||||||
|
if (state() < Starting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while(mState == Starting)
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
TerminateProcess (mTorProcessId, 0);
|
||||||
|
#else
|
||||||
|
kill(mTorProcessId,SIGTERM);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RsInfo() << "Tor process has been normally terminated. Exiting.";
|
||||||
|
|
||||||
|
mState = NotStarted;
|
||||||
|
|
||||||
|
if(m_client) m_client->processStateChanged(mState);// emit stateChanged(d->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorProcess::stateChanged(int newState)
|
||||||
|
{
|
||||||
|
if(m_client)
|
||||||
|
m_client->processStateChanged(newState);
|
||||||
|
}
|
||||||
|
void TorProcess::errorMessageChanged(const std::string& errorMessage)
|
||||||
|
{
|
||||||
|
if(m_client)
|
||||||
|
m_client->processErrorChanged(errorMessage);
|
||||||
|
}
|
||||||
|
void TorProcess::logMessage(const std::string& message)
|
||||||
|
{
|
||||||
|
if(m_client)
|
||||||
|
m_client->processLogMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray TorProcess::controlPassword()
|
||||||
|
{
|
||||||
|
if (mControlPassword.empty())
|
||||||
|
mControlPassword = RsRandom::printable(16);
|
||||||
|
|
||||||
|
return mControlPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::controlHost()
|
||||||
|
{
|
||||||
|
return mControlHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short TorProcess::controlPort()
|
||||||
|
{
|
||||||
|
return mControlPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorProcess::ensureFilesExist()
|
||||||
|
{
|
||||||
|
if(!RsDirUtil::checkCreateDirectory(mDataDir))
|
||||||
|
{
|
||||||
|
mErrorMessage = "Cannot create Tor data directory: " + mDataDir;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RsDirUtil::fileExists(torrcPath()))
|
||||||
|
{
|
||||||
|
FILE *f = RsDirUtil::rs_fopen(torrcPath().c_str(),"w");
|
||||||
|
|
||||||
|
if(!f)
|
||||||
|
{
|
||||||
|
mErrorMessage = "Cannot create Tor configuration file: " + torrcPath();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::torrcPath() const
|
||||||
|
{
|
||||||
|
return RsDirUtil::makePath(mDataDir,"torrc");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TorProcess::controlPortFilePath() const
|
||||||
|
{
|
||||||
|
return RsDirUtil::makePath(mDataDir,"control-port");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorProcess::tryReadControlPort()
|
||||||
|
{
|
||||||
|
FILE *file = RsDirUtil::rs_fopen(controlPortFilePath().c_str(),"r");
|
||||||
|
RsDbg() << "Trying to read control port" ;
|
||||||
|
|
||||||
|
if(file)
|
||||||
|
{
|
||||||
|
char *line = nullptr;
|
||||||
|
size_t tmp_buffsize = 0;
|
||||||
|
|
||||||
|
size_t size = RsFileUtil::rs_getline(&line,&tmp_buffsize,file);
|
||||||
|
ByteArray data = ByteArray((unsigned char*)line,size).trimmed();
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
int p;
|
||||||
|
if (data.startsWith("PORT=") && (p = data.lastIndexOf(':')) > 0) {
|
||||||
|
mControlHost = data.mid(5, p - 5).toString();
|
||||||
|
mControlPort = data.mid(p+1).toInt();
|
||||||
|
|
||||||
|
if (!mControlHost.empty() && mControlPort > 0)
|
||||||
|
{
|
||||||
|
RsDbg() << "Got control host/port = " << mControlHost << ":" << mControlPort ;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
@ -33,24 +33,36 @@
|
|||||||
#ifndef TORPROCESS_H
|
#ifndef TORPROCESS_H
|
||||||
#define TORPROCESS_H
|
#define TORPROCESS_H
|
||||||
|
|
||||||
#include <QObject>
|
#include "bytearray.h"
|
||||||
#include <QHostAddress>
|
#include "util/rsthreads.h"
|
||||||
|
|
||||||
|
class RsFdBinInterface ;
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
#define TorProcessHandle HANDLE
|
||||||
|
#else
|
||||||
|
#define TorProcessHandle pid_t
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Tor
|
namespace Tor
|
||||||
{
|
{
|
||||||
|
|
||||||
class TorProcessPrivate;
|
class TorProcessPrivate;
|
||||||
|
|
||||||
|
// This class is used to inherit calls from the TorProcess
|
||||||
|
|
||||||
|
class TorProcessClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void processStateChanged(int) = 0;
|
||||||
|
virtual void processErrorChanged(const std::string&) = 0;
|
||||||
|
virtual void processLogMessage(const std::string&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
/* Launches and controls a Tor instance with behavior suitable for bundling
|
/* Launches and controls a Tor instance with behavior suitable for bundling
|
||||||
* an instance with the application. */
|
* an instance with the application. */
|
||||||
class TorProcess : public QObject
|
class TorProcess
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
Q_ENUMS(State)
|
|
||||||
|
|
||||||
Q_PROPERTY(State state READ state NOTIFY stateChanged)
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum State {
|
enum State {
|
||||||
Failed = -1,
|
Failed = -1,
|
||||||
@ -60,38 +72,66 @@ public:
|
|||||||
Ready
|
Ready
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit TorProcess(QObject *parent = 0);
|
explicit TorProcess(TorProcessClient *client);
|
||||||
virtual ~TorProcess();
|
virtual ~TorProcess();
|
||||||
|
|
||||||
QString executable() const;
|
std::string executable() const;
|
||||||
void setExecutable(const QString &path);
|
void setExecutable(const std::string &path);
|
||||||
|
|
||||||
QString dataDir() const;
|
std::string dataDir() const;
|
||||||
void setDataDir(const QString &path);
|
void setDataDir(const std::string &path);
|
||||||
|
|
||||||
QString defaultTorrc() const;
|
std::string defaultTorrc() const;
|
||||||
void setDefaultTorrc(const QString &path);
|
void setDefaultTorrc(const std::string &path);
|
||||||
|
|
||||||
QStringList extraSettings() const;
|
std::list<std::string> extraSettings() const;
|
||||||
void setExtraSettings(const QStringList &settings);
|
void setExtraSettings(const std::list<std::string> &settings);
|
||||||
|
|
||||||
State state() const;
|
State state() const;
|
||||||
QString errorMessage() const;
|
std::string errorMessage() const;
|
||||||
QHostAddress controlHost();
|
std::string controlHost();
|
||||||
quint16 controlPort();
|
unsigned short controlPort();
|
||||||
QByteArray controlPassword();
|
ByteArray controlPassword();
|
||||||
|
|
||||||
|
void stateChanged(int newState);
|
||||||
|
void errorMessageChanged(const std::string &errorMessage);
|
||||||
|
void logMessage(const std::string &message);
|
||||||
|
|
||||||
public slots:
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
void tick();
|
||||||
signals:
|
|
||||||
void stateChanged(int newState);
|
|
||||||
void errorMessageChanged(const QString &errorMessage);
|
|
||||||
void logMessage(const QString &message);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TorProcessPrivate *d;
|
TorProcessClient *m_client;
|
||||||
|
|
||||||
|
std::string mExecutable;
|
||||||
|
std::string mDataDir;
|
||||||
|
std::string mDefaultTorrc;
|
||||||
|
std::list<std::string> mExtraSettings;
|
||||||
|
TorProcess::State mState;
|
||||||
|
std::string mErrorMessage;
|
||||||
|
std::string mControlHost;
|
||||||
|
unsigned short mControlPort;
|
||||||
|
ByteArray mControlPassword;
|
||||||
|
|
||||||
|
int controlPortAttempts;
|
||||||
|
|
||||||
|
std::string torrcPath() const;
|
||||||
|
std::string controlPortFilePath() const;
|
||||||
|
bool ensureFilesExist();
|
||||||
|
|
||||||
|
TorProcessHandle mTorProcessId;
|
||||||
|
time_t mLastTryReadControlPort ;
|
||||||
|
int mControlPortReadNbTries ;
|
||||||
|
|
||||||
|
void processStarted();
|
||||||
|
void processFinished();
|
||||||
|
void processError(std::string error);
|
||||||
|
void processReadable();
|
||||||
|
bool tryReadControlPort();
|
||||||
|
|
||||||
|
RsFdBinInterface *mStdOutFD;
|
||||||
|
RsFdBinInterface *mStdErrFD;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
142
libretroshare/src/tor/TorTypes.h
Normal file
142
libretroshare/src/tor/TorTypes.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace Tor
|
||||||
|
{
|
||||||
|
|
||||||
|
class NonCopiable {
|
||||||
|
public:
|
||||||
|
NonCopiable(){}
|
||||||
|
virtual ~NonCopiable()=default;
|
||||||
|
private:
|
||||||
|
NonCopiable(const NonCopiable& nc) {}
|
||||||
|
virtual NonCopiable& operator=(const NonCopiable& nc) { return *this ; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class TorByteArray: public std::vector<unsigned char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TorByteArray(const unsigned char *data,uint32_t len)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
for(uint32_t i=0;i<len;++i)
|
||||||
|
push_back(data[i]);
|
||||||
|
}
|
||||||
|
explicit TorByteArray(const std::string& s)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
for(uint32_t i=0;i<s.length();++i)
|
||||||
|
push_back(s[i]);
|
||||||
|
}
|
||||||
|
TorByteArray(uint32_t s,unsigned char c)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
resize(s,c);
|
||||||
|
}
|
||||||
|
TorByteArray() { clear() ; }
|
||||||
|
|
||||||
|
bool startsWith(const TorByteArray& s) const
|
||||||
|
{
|
||||||
|
if(s.size() > size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<s.size();++i)
|
||||||
|
if(s[i] != data()[i])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexOf(unsigned char c) const
|
||||||
|
{
|
||||||
|
for(uint32_t i=0;i<size();++i)
|
||||||
|
if(data()[i] == c)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TorByteArray& operator+= (unsigned char s) { push_back(s); return *this ;}
|
||||||
|
|
||||||
|
TorByteArray left(uint32_t n) const
|
||||||
|
{
|
||||||
|
auto res = TorByteArray();
|
||||||
|
for(size_t i=0;i<std::min((size_t)n,size());++i)
|
||||||
|
res.push_back(data()[i]);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TorByteArray& operator+=(const TorByteArray& t)
|
||||||
|
{
|
||||||
|
for(uint32_t i=0;i<t.size();++i)
|
||||||
|
push_back(t.data()[i]);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
const TorByteArray& append(const std::string& s) { return operator+=(TorByteArray(s)); }
|
||||||
|
const TorByteArray& append(char s) { return operator+=(s); }
|
||||||
|
|
||||||
|
TorByteArray operator+(const TorByteArray& t) const
|
||||||
|
{
|
||||||
|
auto res = *this;
|
||||||
|
for(uint32_t i=0;i<t.size();++i)
|
||||||
|
res.push_back(t[i]);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toStdString() const
|
||||||
|
{
|
||||||
|
return std::string((const char *)data(),size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const TorByteArray& b) const
|
||||||
|
{
|
||||||
|
if(b.size() > size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<size()-b.size();++i)
|
||||||
|
{
|
||||||
|
bool c = true;
|
||||||
|
|
||||||
|
for(uint32_t j=0;j<b.size();++j)
|
||||||
|
if(b[j] != data()[i+j])
|
||||||
|
{
|
||||||
|
c = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorByteArray mid(uint32_t start,int length=-1) const
|
||||||
|
{
|
||||||
|
if(length==-1)
|
||||||
|
return TorByteArray(data()+start,size()-start);
|
||||||
|
|
||||||
|
if(length < 0 || start + length > size())
|
||||||
|
throw std::runtime_error("Length out of range in TorByteArray::mid()");
|
||||||
|
|
||||||
|
TorByteArray b;
|
||||||
|
for(uint32_t i=0;i<(uint32_t)length;++i)
|
||||||
|
b.push_back(data()[i+start]);
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TorByteArray number(uint64_t n)
|
||||||
|
{
|
||||||
|
std::ostringstream o;
|
||||||
|
o << n ;
|
||||||
|
return TorByteArray(o.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::string TorHostAddress;
|
||||||
|
}
|
@ -30,11 +30,9 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef UTILS_USEFUL_H
|
#pragma once
|
||||||
#define UTILS_USEFUL_H
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include "util/rsdebug.h"
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
/* Print a warning for bug conditions, and assert on a debug build.
|
/* Print a warning for bug conditions, and assert on a debug build.
|
||||||
*
|
*
|
||||||
@ -50,7 +48,7 @@
|
|||||||
* triggered unless the code or logic is wrong.
|
* triggered unless the code or logic is wrong.
|
||||||
*/
|
*/
|
||||||
#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
|
#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
|
||||||
# define BUG() Explode(__FILE__,__LINE__), qWarning() << "BUG:"
|
# define BUG() Explode(__FILE__,__LINE__), RsWarn() << "BUG:"
|
||||||
namespace {
|
namespace {
|
||||||
class Explode
|
class Explode
|
||||||
{
|
{
|
||||||
@ -59,13 +57,10 @@ public:
|
|||||||
int line;
|
int line;
|
||||||
Explode(const char *file, int line) : file(file), line(line) { }
|
Explode(const char *file, int line) : file(file), line(line) { }
|
||||||
~Explode() {
|
~Explode() {
|
||||||
qt_assert("something broke!", file, line);
|
RsErr() << "something broke! in file " << file << line;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
# define BUG() qWarning() << "BUG:"
|
# define BUG() qWarning() << "BUG:"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
195
libretroshare/src/tor/bytearray.h
Normal file
195
libretroshare/src/tor/bytearray.h
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "util/rsprint.h"
|
||||||
|
#include "util/rsdebug.h"
|
||||||
|
|
||||||
|
// This class re-implements QByteArray from Qt library.
|
||||||
|
|
||||||
|
class ByteArray: public std::vector<unsigned char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ByteArray() =default;
|
||||||
|
explicit ByteArray(int n) : std::vector<unsigned char>(n) {}
|
||||||
|
explicit ByteArray(const unsigned char *d,int n) : std::vector<unsigned char>(n) { memcpy(data(),d,n); }
|
||||||
|
virtual ~ByteArray() =default;
|
||||||
|
|
||||||
|
ByteArray(const std::string& c) { resize(c.size()); memcpy(data(),c.c_str(),c.size()); }
|
||||||
|
const ByteArray& operator=(const std::string& c) { resize(c.size()); memcpy(data(),c.c_str(),c.size()); return *this; }
|
||||||
|
|
||||||
|
bool isNull() const { return empty(); }
|
||||||
|
ByteArray toHex() const { return ByteArray(RsUtil::BinToHex(data(),size(),0)); }
|
||||||
|
std::string toString() const { std::string res; for(auto c:*this) res += c; return res; }
|
||||||
|
|
||||||
|
ByteArray operator+(const ByteArray& b) const { auto res(*this); for(unsigned char c:b) res.push_back(c); return res; }
|
||||||
|
ByteArray operator+(const std::string& b) const { return operator+(ByteArray(b)); }
|
||||||
|
|
||||||
|
void append(const ByteArray& b) { for(auto c:b) push_back(c); }
|
||||||
|
void append(const char *b) { for(uint32_t n=0;b[n]!=0;++n) push_back(b[n]); }
|
||||||
|
|
||||||
|
template<class T> void append(const T) = delete;// Prevents any implicit when calling the preceding functions which actually causes real bugs.
|
||||||
|
|
||||||
|
ByteArray& operator+=(char b) { push_back(b); return *this; }
|
||||||
|
ByteArray& operator+=(const ByteArray& b) { for(auto c:b) push_back(c); return *this; }
|
||||||
|
ByteArray& operator+=(const char *b) { for(uint32_t n=0;b[n]!=0;++n) push_back(b[n]); return *this;}
|
||||||
|
|
||||||
|
ByteArray left(uint32_t l) const { auto res = *this; res.resize(std::min((uint32_t)size(),l)); return res; }
|
||||||
|
ByteArray toUpper() const { auto res = *this; for(uint32_t i=0;i<size();++i) if( res[i]<='z' && res[i]>='a') res[i] += int('A')-int('a'); return res; }
|
||||||
|
ByteArray toLower() const { auto res = *this; for(uint32_t i=0;i<size();++i) if( res[i]<='Z' && res[i]>='A') res[i] += int('a')-int('A'); return res; }
|
||||||
|
|
||||||
|
int toInt() const
|
||||||
|
{
|
||||||
|
std::istringstream is(toString().c_str());
|
||||||
|
|
||||||
|
int res = -1;
|
||||||
|
is >> res ;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool endsWith(const ByteArray& b) const { return size() >= b.size() && !memcmp(&data()[size()-b.size()],b.data(),b.size()); }
|
||||||
|
bool endsWith(char b) const { return size() > 0 && back()==b; }
|
||||||
|
bool startsWith(const ByteArray& b) const { return b.size() <= size() && !strncmp((char*)b.data(),(char*)data(),std::min(size(),b.size())); }
|
||||||
|
bool startsWith(const char *b) const
|
||||||
|
{
|
||||||
|
for(uint32_t n=0;b[n]!=0;++n)
|
||||||
|
if(n >= size() || b[n]!=(*this)[n])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const char *b) const
|
||||||
|
{
|
||||||
|
uint32_t n;
|
||||||
|
for(n=0;b[n]!=0;++n)
|
||||||
|
if(n >= size() || b[n]!=(*this)[n])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return n==size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray mid(uint32_t n,int s=-1) const
|
||||||
|
{
|
||||||
|
ByteArray res((s>=0)?s:(size()-n));
|
||||||
|
memcpy(res.data(),&data()[n],res.size());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexOf(unsigned char c,int from=0) const
|
||||||
|
{
|
||||||
|
for(uint32_t i=from;i<size();++i)
|
||||||
|
if((*this)[i]==c)
|
||||||
|
return (int)i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray replace(const ByteArray& b1,const ByteArray& b2)
|
||||||
|
{
|
||||||
|
if(b1.empty())
|
||||||
|
{
|
||||||
|
RsErr() << "Attempting to replace an empty string!";
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ByteArray res ;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i+b1.size()<=size();)
|
||||||
|
if(!memcmp(&(*this)[i],b1.data(),b1.size()))
|
||||||
|
{
|
||||||
|
res.append(b2);
|
||||||
|
i += b1.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res.push_back((*this)[i++]);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits the byte array using sep as separator.
|
||||||
|
|
||||||
|
std::list<ByteArray> split(unsigned char sep)
|
||||||
|
{
|
||||||
|
std::list<ByteArray> res;
|
||||||
|
ByteArray current_block;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<size();++i)
|
||||||
|
if(operator[](i) == sep)
|
||||||
|
{
|
||||||
|
res.push_back(current_block);
|
||||||
|
current_block.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_block += (*this)[i];
|
||||||
|
|
||||||
|
if(!current_block.empty())
|
||||||
|
res.push_back(current_block);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits the byte array using any of the characters in sep as separators.
|
||||||
|
|
||||||
|
std::list<ByteArray> split(const ByteArray& sep)
|
||||||
|
{
|
||||||
|
std::list<ByteArray> res;
|
||||||
|
ByteArray current_block;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<size();++i)
|
||||||
|
if(std::find(sep.begin(),sep.end(),operator[](i)) != sep.end())
|
||||||
|
{
|
||||||
|
res.push_back(current_block);
|
||||||
|
current_block.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_block += operator[](i);
|
||||||
|
|
||||||
|
if(!current_block.empty())
|
||||||
|
res.push_back(current_block);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes the following characters from the beginning and from the end of the array:
|
||||||
|
// '\t', '\n', '\v', '\f', '\r', and ' '.
|
||||||
|
|
||||||
|
ByteArray trimmed() const
|
||||||
|
{
|
||||||
|
auto res(*this);
|
||||||
|
|
||||||
|
while(!res.empty() && ( res.back() == '\t' || res.back() == '\n' || res.back() == '\v'
|
||||||
|
|| res.back() == '\f' || res.back() == '\r' || res.back() == ' ' ) )
|
||||||
|
res.pop_back();
|
||||||
|
|
||||||
|
uint32_t i=0;
|
||||||
|
|
||||||
|
for(;i<res.size();++i)
|
||||||
|
if(res[i] != '\t' && res[i] != '\n' && res[i] != '\v' && res[i] != '\f' && res[i] != '\r' && res[i] != ' ')
|
||||||
|
break;
|
||||||
|
|
||||||
|
return res.mid(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes n bytes from the end of the array
|
||||||
|
|
||||||
|
void chop(uint32_t n)
|
||||||
|
{
|
||||||
|
resize(std::max(0,(int)size() - (int)n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the last index of a given byte, -1 if not found.
|
||||||
|
|
||||||
|
int lastIndexOf(unsigned char s)
|
||||||
|
{
|
||||||
|
for(int i=size()-1;i>=0;--i)
|
||||||
|
if(operator[](i) == s)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
@ -60,6 +60,12 @@
|
|||||||
#define canonicalize_file_name(p) realpath(p, NULL)
|
#define canonicalize_file_name(p) realpath(p, NULL)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
#define FIND_OF_DIRECTORY_SEPARATOR "/\\\0"
|
||||||
|
#else
|
||||||
|
#define FIND_OF_DIRECTORY_SEPARATOR '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
/****
|
/****
|
||||||
* #define RSDIR_DEBUG 1
|
* #define RSDIR_DEBUG 1
|
||||||
****/
|
****/
|
||||||
@ -68,7 +74,7 @@
|
|||||||
bool std::filesystem::create_directories(const std::string& path)
|
bool std::filesystem::create_directories(const std::string& path)
|
||||||
{
|
{
|
||||||
for( std::string::size_type lastIndex = 0; lastIndex < std::string::npos;
|
for( std::string::size_type lastIndex = 0; lastIndex < std::string::npos;
|
||||||
lastIndex = path.find('/', lastIndex) )
|
lastIndex = path.find_first_of(FIND_OF_DIRECTORY_SEPARATOR, lastIndex) )
|
||||||
{
|
{
|
||||||
std::string&& curDir = path.substr(0, ++lastIndex);
|
std::string&& curDir = path.substr(0, ++lastIndex);
|
||||||
if(!RsDirUtil::checkCreateDirectory(curDir))
|
if(!RsDirUtil::checkCreateDirectory(curDir))
|
||||||
@ -84,6 +90,25 @@ bool std::filesystem::create_directories(const std::string& path)
|
|||||||
# include <filesystem>
|
# include <filesystem>
|
||||||
#endif // __cplusplus < 201703L
|
#endif // __cplusplus < 201703L
|
||||||
|
|
||||||
|
std::string RsDirUtil::getFileName(const std::string& full_file_path)
|
||||||
|
{
|
||||||
|
std::string::size_type n = full_file_path.find_last_of(FIND_OF_DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
if(n == std::string::npos)
|
||||||
|
return full_file_path;
|
||||||
|
else
|
||||||
|
return full_file_path.substr(n+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RsDirUtil::getDirectory(const std::string& full_file_path)
|
||||||
|
{
|
||||||
|
std::string::size_type n = full_file_path.find_last_of(FIND_OF_DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
if(n == std::string::npos)
|
||||||
|
return std::string();
|
||||||
|
else
|
||||||
|
return full_file_path.substr(0,n);
|
||||||
|
}
|
||||||
std::string RsDirUtil::getTopDir(const std::string& dir)
|
std::string RsDirUtil::getTopDir(const std::string& dir)
|
||||||
{
|
{
|
||||||
std::string top;
|
std::string top;
|
||||||
@ -92,10 +117,10 @@ std::string RsDirUtil::getTopDir(const std::string& dir)
|
|||||||
*/
|
*/
|
||||||
int i,j;
|
int i,j;
|
||||||
int len = dir.length();
|
int len = dir.length();
|
||||||
for(j = len - 1; (j > 0) && (dir[j] == '/'); j--) ;
|
for(j = len - 1; (j > 0) && RsDirUtil::isDirectorySeparator(dir[j]); j--) ;
|
||||||
for(i = j; (i > 0) && (dir[i] != '/'); i--) ;
|
for(i = j; (i > 0) && !RsDirUtil::isDirectorySeparator(dir[i]); i--) ;
|
||||||
|
|
||||||
if (dir[i] == '/')
|
if (RsDirUtil::isDirectorySeparator(dir[i]))
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
for(; i <= j; i++)
|
for(; i <= j; i++)
|
||||||
@ -125,9 +150,9 @@ const char *RsDirUtil::scanf_string_for_uint(int bytes)
|
|||||||
|
|
||||||
bool RsDirUtil::splitDirFromFile(const std::string& full_path,std::string& dir, std::string& file)
|
bool RsDirUtil::splitDirFromFile(const std::string& full_path,std::string& dir, std::string& file)
|
||||||
{
|
{
|
||||||
int i = full_path.rfind('/', full_path.size()-1);
|
std::string::size_type i = full_path.find_last_of(FIND_OF_DIRECTORY_SEPARATOR, full_path.size()-1);
|
||||||
|
|
||||||
if(i == full_path.size()-1) // '/' not found!
|
if(i == std::string::npos) // '/' not found!
|
||||||
{
|
{
|
||||||
file = full_path ;
|
file = full_path ;
|
||||||
dir = "." ;
|
dir = "." ;
|
||||||
@ -146,13 +171,13 @@ void RsDirUtil::removeTopDir(const std::string& dir, std::string& path)
|
|||||||
|
|
||||||
/* remove the subdir: [/][dir1.../]<top>[/]
|
/* remove the subdir: [/][dir1.../]<top>[/]
|
||||||
*/
|
*/
|
||||||
int j = dir.find_last_not_of('/');
|
int j = dir.find_last_not_of(FIND_OF_DIRECTORY_SEPARATOR);
|
||||||
int i = dir.rfind('/', j);
|
int i = dir.find_last_of(FIND_OF_DIRECTORY_SEPARATOR, j);
|
||||||
|
|
||||||
/* remove any more slashes */
|
/* remove any more slashes */
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
{
|
{
|
||||||
i = dir.find_last_not_of('/', i);
|
i = dir.find_last_not_of(FIND_OF_DIRECTORY_SEPARATOR, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
@ -169,8 +194,8 @@ std::string RsDirUtil::getRootDir(const std::string& dir)
|
|||||||
*/
|
*/
|
||||||
int i,j;
|
int i,j;
|
||||||
int len = dir.length();
|
int len = dir.length();
|
||||||
for(i = 0; (i < len) && (dir[i] == '/'); i++) ;
|
for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(dir[i]); i++) ;
|
||||||
for(j = i; (j < len) && (dir[j] != '/'); j++) ;
|
for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(dir[j]); j++) ;
|
||||||
if (i == j)
|
if (i == j)
|
||||||
return root; /* empty */
|
return root; /* empty */
|
||||||
for(; i < j; i++)
|
for(; i < j; i++)
|
||||||
@ -187,12 +212,12 @@ std::string RsDirUtil::removeRootDir(const std::string& path)
|
|||||||
std::string output;
|
std::string output;
|
||||||
|
|
||||||
/* chew leading '/'s */
|
/* chew leading '/'s */
|
||||||
for(i = 0; (i < len) && (path[i] == '/'); i++) ;
|
for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(path[i]); i++) ;
|
||||||
if (i == len)
|
if (i == len)
|
||||||
return output; /* empty string */
|
return output; /* empty string */
|
||||||
|
|
||||||
for(j = i; (j < len) && (path[j] != '/'); j++) ; /* run to next '/' */
|
for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(path[j]); j++) ; /* run to next '/' */
|
||||||
for(; (j < len) && (path[j] == '/'); j++) ; /* chew leading '/'s */
|
for(; (j < len) && RsDirUtil::isDirectorySeparator(path[j]); j++) ; /* chew leading '/'s */
|
||||||
|
|
||||||
for(; j < len; j++)
|
for(; j < len; j++)
|
||||||
{
|
{
|
||||||
@ -213,7 +238,7 @@ std::string RsDirUtil::removeRootDirs(const std::string& path, const std::string
|
|||||||
if ((root.length() < 1) || (path.length() < 1))
|
if ((root.length() < 1) || (path.length() < 1))
|
||||||
return notroot;
|
return notroot;
|
||||||
|
|
||||||
if ((path[0] == '/') && (root[0] != '/'))
|
if (RsDirUtil::isDirectorySeparator(path[0]) && !RsDirUtil::isDirectorySeparator(root[0]))
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -232,7 +257,7 @@ std::string RsDirUtil::removeRootDirs(const std::string& path, const std::string
|
|||||||
return notroot;
|
return notroot;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path[i] == '/')
|
if (RsDirUtil::isDirectorySeparator(path[i]))
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -256,7 +281,7 @@ int RsDirUtil::breakupDirList(const std::string& path,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
for(i = 0; i < path.length(); i++)
|
for(i = 0; i < path.length(); i++)
|
||||||
{
|
{
|
||||||
if (path[i] == '/')
|
if (RsDirUtil::isDirectorySeparator(path[i]))
|
||||||
{
|
{
|
||||||
if (i - start > 0)
|
if (i - start > 0)
|
||||||
{
|
{
|
||||||
@ -871,11 +896,26 @@ std::string RsDirUtil::convertPathToUnix(std::string path)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RsDirUtil::isDirectorySeparator(const char &c)
|
||||||
|
{
|
||||||
|
if (c == '/') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
if (c == '\\') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string RsDirUtil::makePath(const std::string &path1, const std::string &path2)
|
std::string RsDirUtil::makePath(const std::string &path1, const std::string &path2)
|
||||||
{
|
{
|
||||||
std::string path = path1;
|
std::string path = path1;
|
||||||
|
|
||||||
if (path.empty() == false && *path.rbegin() != '/') {
|
if (path.empty() == false && !RsDirUtil::isDirectorySeparator(*path.rbegin())) {
|
||||||
path += "/";
|
path += "/";
|
||||||
}
|
}
|
||||||
path += path2;
|
path += path2;
|
||||||
@ -1012,10 +1052,10 @@ std::wstring RsDirUtil::getWideTopDir(std::wstring dir)
|
|||||||
*/
|
*/
|
||||||
int i,j;
|
int i,j;
|
||||||
int len = dir.length();
|
int len = dir.length();
|
||||||
for(j = len - 1; (j > 0) && (dir[j] == '/'); j--);
|
for(j = len - 1; (j > 0) && RsDirUtil::isDirectorySeparator(dir[j]); j--);
|
||||||
for(i = j; (i > 0) && (dir[i] != '/'); i--);
|
for(i = j; (i > 0) && !RsDirUtil::isDirectorySeparator(dir[i]); i--);
|
||||||
|
|
||||||
if (dir[i] == '/')
|
if (RsDirUtil::isDirectorySeparator(dir[i]))
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
for(; i <= j; i++)
|
for(; i <= j; i++)
|
||||||
@ -1034,11 +1074,11 @@ std::wstring RsDirUtil::removeWideTopDir(std::wstring dir)
|
|||||||
*/
|
*/
|
||||||
int i,j;
|
int i,j;
|
||||||
int len = dir.length();
|
int len = dir.length();
|
||||||
for(j = len - 1; (j > 0) && (dir[j] == '/'); j--);
|
for(j = len - 1; (j > 0) && RsDirUtil::isDirectorySeparator(dir[j]); j--);
|
||||||
for(i = j; (i >= 0) && (dir[i] != '/'); i--);
|
for(i = j; (i >= 0) && !RsDirUtil::isDirectorySeparator(dir[i]); i--);
|
||||||
|
|
||||||
/* remove any more slashes */
|
/* remove any more slashes */
|
||||||
for(; (i >= 0) && (dir[i] == '/'); i--);
|
for(; (i >= 0) && RsDirUtil::isDirectorySeparator(dir[i]); i--);
|
||||||
|
|
||||||
for(j = 0; j <= i; j++)
|
for(j = 0; j <= i; j++)
|
||||||
{
|
{
|
||||||
@ -1056,8 +1096,8 @@ std::wstring RsDirUtil::getWideRootDir(std::wstring dir)
|
|||||||
*/
|
*/
|
||||||
int i,j;
|
int i,j;
|
||||||
int len = dir.length();
|
int len = dir.length();
|
||||||
for(i = 0; (i < len) && (dir[i] == '/'); i++);
|
for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(dir[i]); i++);
|
||||||
for(j = i; (j < len) && (dir[j] != '/'); j++);
|
for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(dir[j]); j++);
|
||||||
if (i == j)
|
if (i == j)
|
||||||
return root; /* empty */
|
return root; /* empty */
|
||||||
for(; i < j; i++)
|
for(; i < j; i++)
|
||||||
@ -1074,12 +1114,12 @@ std::wstring RsDirUtil::removeWideRootDir(std::wstring path)
|
|||||||
std::wstring output;
|
std::wstring output;
|
||||||
|
|
||||||
/* chew leading '/'s */
|
/* chew leading '/'s */
|
||||||
for(i = 0; (i < len) && (path[i] == '/'); i++);
|
for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(path[i]); i++);
|
||||||
if (i == len)
|
if (i == len)
|
||||||
return output; /* empty string */
|
return output; /* empty string */
|
||||||
|
|
||||||
for(j = i; (j < len) && (path[j] != '/'); j++); /* run to next '/' */
|
for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(path[j]); j++); /* run to next '/' */
|
||||||
for(; (j < len) && (path[j] == '/'); j++); /* chew leading '/'s */
|
for(; (j < len) && RsDirUtil::isDirectorySeparator(path[j]); j++); /* chew leading '/'s */
|
||||||
|
|
||||||
for(; j < len; j++)
|
for(; j < len; j++)
|
||||||
{
|
{
|
||||||
@ -1100,7 +1140,7 @@ std::wstring RsDirUtil::removeWideRootDirs(std::wstring path, std::wstring root)
|
|||||||
if ((root.length() < 1) || (path.length() < 1))
|
if ((root.length() < 1) || (path.length() < 1))
|
||||||
return notroot;
|
return notroot;
|
||||||
|
|
||||||
if ((path[0] == '/') && (root[0] != '/'))
|
if (RsDirUtil::isDirectorySeparator(path[0]) && !RsDirUtil::isDirectorySeparator(root[0]))
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -1119,7 +1159,7 @@ std::wstring RsDirUtil::removeWideRootDirs(std::wstring path, std::wstring root)
|
|||||||
return notroot;
|
return notroot;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path[i] == '/')
|
if (RsDirUtil::isDirectorySeparator(path[i]))
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -1143,7 +1183,7 @@ int RsDirUtil::breakupWideDirList(std::wstring path,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
for(i = 0; i < path.length(); i++)
|
for(i = 0; i < path.length(); i++)
|
||||||
{
|
{
|
||||||
if (path[i] == '/')
|
if (RsDirUtil::isDirectorySeparator(path[i]))
|
||||||
{
|
{
|
||||||
if (i - start > 0)
|
if (i - start > 0)
|
||||||
{
|
{
|
||||||
|
@ -58,12 +58,20 @@ class RsStackFileLock
|
|||||||
|
|
||||||
namespace RsDirUtil {
|
namespace RsDirUtil {
|
||||||
|
|
||||||
|
// Returns the name of the directory on top of the given path (as opposed to the full path to that directory)
|
||||||
std::string getTopDir(const std::string&);
|
std::string getTopDir(const std::string&);
|
||||||
std::string getRootDir(const std::string&);
|
std::string getRootDir(const std::string&);
|
||||||
std::string removeRootDir(const std::string& path);
|
std::string removeRootDir(const std::string& path);
|
||||||
void removeTopDir(const std::string& dir, std::string &path);
|
void removeTopDir(const std::string& dir, std::string &path);
|
||||||
std::string removeRootDirs(const std::string& path, const std::string& root);
|
std::string removeRootDirs(const std::string& path, const std::string& root);
|
||||||
|
|
||||||
|
// Returns the filename at the end of the path. An empty string is returned if the path is a directory path.
|
||||||
|
std::string getFileName(const std::string& full_file_path);
|
||||||
|
|
||||||
|
// Returns the directory (full path) that contains the given path (filename or directory).
|
||||||
|
// If a directory is supplied, the same path is returned.
|
||||||
|
std::string getDirectory(const std::string& full_file_path);
|
||||||
|
|
||||||
// Renames file from to file to. Files should be on the same file system.
|
// Renames file from to file to. Files should be on the same file system.
|
||||||
// returns true if succeed, false otherwise.
|
// returns true if succeed, false otherwise.
|
||||||
bool renameFile(const std::string& from,const std::string& to) ;
|
bool renameFile(const std::string& from,const std::string& to) ;
|
||||||
@ -107,6 +115,12 @@ rstime_t lastWriteTime(
|
|||||||
std::error_condition& errc = RS_DEFAULT_STORAGE_PARAM(std::error_condition) );
|
std::error_condition& errc = RS_DEFAULT_STORAGE_PARAM(std::error_condition) );
|
||||||
|
|
||||||
bool checkDirectory(const std::string& dir);
|
bool checkDirectory(const std::string& dir);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief checkCreateDirectory
|
||||||
|
* \param dir
|
||||||
|
* \return false when the directory does not exist and could not be created.
|
||||||
|
*/
|
||||||
bool checkCreateDirectory(const std::string& dir);
|
bool checkCreateDirectory(const std::string& dir);
|
||||||
|
|
||||||
// Removes all symbolic links along the path and computes the actual location of the file/dir passed as argument.
|
// Removes all symbolic links along the path and computes the actual location of the file/dir passed as argument.
|
||||||
@ -159,6 +173,7 @@ bool getWideFileHash(std::wstring filepath, RsFileHash &hash, u
|
|||||||
FILE *rs_fopen(const char* filename, const char* mode);
|
FILE *rs_fopen(const char* filename, const char* mode);
|
||||||
|
|
||||||
std::string convertPathToUnix(std::string path);
|
std::string convertPathToUnix(std::string path);
|
||||||
|
bool isDirectorySeparator(const char &c);
|
||||||
|
|
||||||
/** Concatenate two path pieces putting '/' separator between them only if
|
/** Concatenate two path pieces putting '/' separator between them only if
|
||||||
* needed */
|
* needed */
|
||||||
|
107
libretroshare/src/util/rsfile.cc
Normal file
107
libretroshare/src/util/rsfile.cc
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* libretroshare/src/util: rsfile.cc *
|
||||||
|
* *
|
||||||
|
* libretroshare: retroshare core library *
|
||||||
|
* *
|
||||||
|
* Copyright (C) 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 Lesser 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 Lesser General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License *
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||||
|
* *
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include "util/rsfile.h"
|
||||||
|
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
#include <wtypes.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <namedpipeapi.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int RsFileUtil::set_fd_nonblock(int fd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/******************* OS SPECIFIC PART ******************/
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
DWORD mode = PIPE_NOWAIT;
|
||||||
|
WINBOOL result = SetNamedPipeHandleState((HANDLE) _get_osfhandle(fd), &mode, nullptr, nullptr);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
#else // ie UNIX
|
||||||
|
int flags = fcntl(fd, F_GETFL);
|
||||||
|
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t RsFileUtil::rs_getline(char **lineptr, size_t *n, FILE *stream)
|
||||||
|
{
|
||||||
|
/******************* OS SPECIFIC PART ******************/
|
||||||
|
#ifdef WINDOWS_SYS
|
||||||
|
if (lineptr == nullptr || n == nullptr || stream == nullptr) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*lineptr == nullptr || *n < 1) {
|
||||||
|
*n = BUFSIZ;
|
||||||
|
*lineptr = (char*) malloc(*n);
|
||||||
|
if (*lineptr == nullptr) {
|
||||||
|
*n = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ptr = *lineptr;
|
||||||
|
while (true) {
|
||||||
|
int c = fgetc(stream);
|
||||||
|
if (c == -1) {
|
||||||
|
if (feof(stream)) {
|
||||||
|
*ptr = '\0';
|
||||||
|
return (ssize_t) (ptr - *lineptr);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = c;
|
||||||
|
++ptr;
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
*ptr = '\0';
|
||||||
|
return ptr - *lineptr;
|
||||||
|
}
|
||||||
|
if (ptr + 2 >= *lineptr + *n) {
|
||||||
|
size_t new_size = *n * 2;
|
||||||
|
ssize_t diff = ptr - *lineptr;
|
||||||
|
|
||||||
|
char *new_lineptr = (char*) realloc(*lineptr, new_size);
|
||||||
|
if (new_lineptr == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*lineptr = new_lineptr;
|
||||||
|
*n = new_size;
|
||||||
|
ptr = new_lineptr + diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else // ie UNIX
|
||||||
|
return getline(lineptr, n, stream);
|
||||||
|
#endif
|
||||||
|
}
|
32
libretroshare/src/util/rsfile.h
Normal file
32
libretroshare/src/util/rsfile.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* libretroshare/src/util: rsfile.h *
|
||||||
|
* *
|
||||||
|
* libretroshare: retroshare core library *
|
||||||
|
* *
|
||||||
|
* Copyright (C) 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 Lesser 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 Lesser General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License *
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||||
|
* *
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace RsFileUtil {
|
||||||
|
|
||||||
|
int set_fd_nonblock(int fd);
|
||||||
|
ssize_t rs_getline(char **lineptr, size_t *n, FILE *stream);
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "util/rsprint.h"
|
#include "util/rsprint.h"
|
||||||
#include "util/rsstring.h"
|
#include "util/rsstring.h"
|
||||||
|
#include <stdio.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
@ -45,6 +46,13 @@ std::string RsUtil::NumberToString(uint64_t n,bool hex)
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RsUtil::StringToInt(const std::string& s,int& n)
|
||||||
|
{
|
||||||
|
if(sscanf(s.c_str(),"%d",&n) == 1)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
std::string RsUtil::BinToHex(const std::string &bin)
|
std::string RsUtil::BinToHex(const std::string &bin)
|
||||||
{
|
{
|
||||||
return BinToHex(bin.c_str(), bin.length());
|
return BinToHex(bin.c_str(), bin.length());
|
||||||
|
@ -36,6 +36,10 @@ std::string BinToHex(const char *arr, const uint32_t len);
|
|||||||
std::string BinToHex(const unsigned char *arr, const uint32_t len, uint32_t max_len=0);
|
std::string BinToHex(const unsigned char *arr, const uint32_t len, uint32_t max_len=0);
|
||||||
bool HexToBin(const std::string& input,unsigned char *data, const uint32_t len);
|
bool HexToBin(const std::string& input,unsigned char *data, const uint32_t len);
|
||||||
std::string NumberToString(uint64_t n, bool hex=false);
|
std::string NumberToString(uint64_t n, bool hex=false);
|
||||||
|
|
||||||
|
// Returns in n the int that can be read in the string. Returns false when no int is fond.
|
||||||
|
bool StringToInt(const std::string& s,int& n);
|
||||||
|
|
||||||
std::string HashId(const std::string &id, bool reverse = false);
|
std::string HashId(const std::string &id, bool reverse = false);
|
||||||
std::vector<uint8_t> BinToSha256(const std::vector<uint8_t> &in);
|
std::vector<uint8_t> BinToSha256(const std::vector<uint8_t> &in);
|
||||||
|
|
||||||
|
@ -139,8 +139,12 @@ double RsRandom::random_f64()
|
|||||||
|
|
||||||
/*static*/ std::string RsRandom::printable(uint32_t length)
|
/*static*/ std::string RsRandom::printable(uint32_t length)
|
||||||
{
|
{
|
||||||
std::string ret(length, 0);
|
std::string res;
|
||||||
random_bytes(reinterpret_cast<uint8_t*>(&ret[0]), length);
|
RsTemporaryMemory mem(length);
|
||||||
for(uint32_t i=0; i<length; ++i) ret[i] = (ret[i] % 94) + 33;
|
random_bytes(mem,length);
|
||||||
return ret;
|
|
||||||
|
for(uint32_t i=0; i<length; ++i)
|
||||||
|
res += (char)(( ((int) ((uint8_t*)mem)[i]) % 94 ) + 33);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -1,527 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "SecureRNG.h"
|
|
||||||
#include "Useful.h"
|
|
||||||
#include <QtDebug>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
|
||||||
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
|
|
||||||
{
|
|
||||||
*p = r->p;
|
|
||||||
*q = r->q;
|
|
||||||
}
|
|
||||||
#define RSA_bits(o) (BN_num_bits((o)->n))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen);
|
|
||||||
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CryptoKey::CryptoKey()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoKey::~CryptoKey()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
CryptoKey::Data::~Data()
|
|
||||||
{
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
RSA_free(key);
|
|
||||||
key = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void CryptoKey::clear()
|
|
||||||
{
|
|
||||||
key_data.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
bool CryptoKey::loadFromData(const QByteArray &data, KeyType type, KeyFormat format)
|
|
||||||
{
|
|
||||||
RSA *key = NULL;
|
|
||||||
clear();
|
|
||||||
|
|
||||||
if (data.isEmpty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (format == PEM) {
|
|
||||||
BIO *b = BIO_new_mem_buf((void*)data.constData(), -1);
|
|
||||||
|
|
||||||
if (type == PrivateKey)
|
|
||||||
key = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL);
|
|
||||||
else
|
|
||||||
key = PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
BIO_free(b);
|
|
||||||
} else if (format == DER) {
|
|
||||||
const uchar *dp = reinterpret_cast<const uchar*>(data.constData());
|
|
||||||
|
|
||||||
if (type == PrivateKey)
|
|
||||||
key = d2i_RSAPrivateKey(NULL, &dp, data.size());
|
|
||||||
else
|
|
||||||
key = d2i_RSAPublicKey(NULL, &dp, data.size());
|
|
||||||
} else {
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!key) {
|
|
||||||
qWarning() << "Failed to parse" << (type == PrivateKey ? "private" : "public") << "key from data";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
d = new Data(key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool CryptoKey::loadFromFile(const QString& path)
|
|
||||||
{
|
|
||||||
QFile file(path);
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
qWarning() << "Failed to open Tor key file " << path << ": " << file.errorString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray data = file.readAll();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if(data.contains("-----BEGIN RSA PRIVATE KEY-----"))
|
|
||||||
{
|
|
||||||
std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl;
|
|
||||||
|
|
||||||
// This to be compliant with old format. New format is oblivious to the type of key so we dont need a header
|
|
||||||
data = data.replace("-----BEGIN RSA PRIVATE KEY-----",nullptr);
|
|
||||||
data = data.replace("-----END RSA PRIVATE KEY-----",nullptr);
|
|
||||||
data = data.replace("\n",nullptr);
|
|
||||||
data = data.replace("\t",nullptr);
|
|
||||||
|
|
||||||
data = "RSA1024:"+data;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << "Have read the following key: " << std::endl;
|
|
||||||
std::cerr << QString(data).toStdString() << std::endl;
|
|
||||||
|
|
||||||
key_data = data;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CryptoKey::loadFromTorMessage(const QByteArray& b)
|
|
||||||
{
|
|
||||||
// note: We should probably check the structure a bit more, for security.
|
|
||||||
|
|
||||||
std::cerr << "Loading new key:" << std::endl;
|
|
||||||
|
|
||||||
if(b.startsWith("RSA1024"))
|
|
||||||
std::cerr << " type: RSA-1024 (Tor v2)" << std::endl;
|
|
||||||
else if(b.startsWith("ED25519-V3"))
|
|
||||||
std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl;
|
|
||||||
else if(b.indexOf(':'))
|
|
||||||
{
|
|
||||||
std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toStdString() << "\". Not accepted." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
key_data = b;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */
|
|
||||||
QByteArray torControlHashedPassword(const QByteArray &password)
|
|
||||||
{
|
|
||||||
QByteArray salt = SecureRNG::random(8);
|
|
||||||
if (salt.isNull())
|
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
int count = ((quint32)16 + (96 & 15)) << ((96 >> 4) + 6);
|
|
||||||
|
|
||||||
SHA_CTX hash;
|
|
||||||
SHA1_Init(&hash);
|
|
||||||
|
|
||||||
QByteArray tmp = salt + password;
|
|
||||||
while (count)
|
|
||||||
{
|
|
||||||
int c = qMin(count, tmp.size());
|
|
||||||
SHA1_Update(&hash, reinterpret_cast<const void*>(tmp.constData()), c);
|
|
||||||
count -= c;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char md[20];
|
|
||||||
SHA1_Final(md, &hash);
|
|
||||||
|
|
||||||
/* 60 is the hex-encoded value of 96, which is a constant used by Tor's algorithm. */
|
|
||||||
return QByteArray("16:") + salt.toHex().toUpper() + QByteArray("60") +
|
|
||||||
QByteArray::fromRawData(reinterpret_cast<const char*>(md), 20).toHex().toUpper();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
bool CryptoKey::isPrivate() const
|
|
||||||
{
|
|
||||||
if (!isLoaded()) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
const BIGNUM *p, *q;
|
|
||||||
RSA_get0_factors(d->key, &p, &q);
|
|
||||||
return (p != 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int CryptoKey::bits() const
|
|
||||||
{
|
|
||||||
return isLoaded() ? RSA_bits(d->key) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray CryptoKey::publicKeyDigest() const
|
|
||||||
{
|
|
||||||
if (!isLoaded())
|
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
QByteArray buf = encodedPublicKey(DER);
|
|
||||||
|
|
||||||
QByteArray re(20, 0);
|
|
||||||
bool ok = SHA1(reinterpret_cast<const unsigned char*>(buf.constData()), buf.size(),
|
|
||||||
reinterpret_cast<unsigned char*>(re.data())) != NULL;
|
|
||||||
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
qWarning() << "Failed to hash public key data for digest";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return re;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray CryptoKey::encodedPublicKey(KeyFormat format) const
|
|
||||||
{
|
|
||||||
if (!isLoaded())
|
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
if (format == PEM) {
|
|
||||||
BIO *b = BIO_new(BIO_s_mem());
|
|
||||||
|
|
||||||
if (!PEM_write_bio_RSAPublicKey(b, d->key)) {
|
|
||||||
BUG() << "Failed to encode public key in PEM format";
|
|
||||||
BIO_free(b);
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
BUF_MEM *buf;
|
|
||||||
BIO_get_mem_ptr(b, &buf);
|
|
||||||
|
|
||||||
/* Close BIO, but don't free buf. */
|
|
||||||
(void)BIO_set_close(b, BIO_NOCLOSE);
|
|
||||||
BIO_free(b);
|
|
||||||
|
|
||||||
QByteArray re((const char *)buf->data, (int)buf->length);
|
|
||||||
BUF_MEM_free(buf);
|
|
||||||
return re;
|
|
||||||
} else if (format == DER) {
|
|
||||||
uchar *buf = NULL;
|
|
||||||
int len = i2d_RSAPublicKey(d->key, &buf);
|
|
||||||
if (len <= 0 || !buf) {
|
|
||||||
BUG() << "Failed to encode public key in DER format";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray re((const char*)buf, len);
|
|
||||||
OPENSSL_free(buf);
|
|
||||||
return re;
|
|
||||||
} else {
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray CryptoKey::encodedPrivateKey(KeyFormat format) const
|
|
||||||
{
|
|
||||||
if (!isLoaded() || !isPrivate())
|
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
if (format == PEM) {
|
|
||||||
BIO *b = BIO_new(BIO_s_mem());
|
|
||||||
|
|
||||||
if (!PEM_write_bio_RSAPrivateKey(b, d->key, NULL, NULL, 0, NULL, NULL)) {
|
|
||||||
BUG() << "Failed to encode private key in PEM format";
|
|
||||||
BIO_free(b);
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
BUF_MEM *buf;
|
|
||||||
BIO_get_mem_ptr(b, &buf);
|
|
||||||
|
|
||||||
/* Close BIO, but don't free buf. */
|
|
||||||
(void)BIO_set_close(b, BIO_NOCLOSE);
|
|
||||||
BIO_free(b);
|
|
||||||
|
|
||||||
QByteArray re((const char *)buf->data, (int)buf->length);
|
|
||||||
BUF_MEM_free(buf);
|
|
||||||
return re;
|
|
||||||
} else if (format == DER) {
|
|
||||||
uchar *buf = NULL;
|
|
||||||
int len = i2d_RSAPrivateKey(d->key, &buf);
|
|
||||||
if (len <= 0 || !buf) {
|
|
||||||
BUG() << "Failed to encode private key in DER format";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray re((const char*)buf, len);
|
|
||||||
OPENSSL_free(buf);
|
|
||||||
return re;
|
|
||||||
} else {
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CryptoKey::torServiceID() const
|
|
||||||
{
|
|
||||||
if (!isLoaded())
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
QByteArray digest = publicKeyDigest();
|
|
||||||
if (digest.isNull())
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
static const int hostnameDigestSize = 10;
|
|
||||||
static const int hostnameEncodedSize = 16;
|
|
||||||
|
|
||||||
QByteArray re(hostnameEncodedSize+1, 0);
|
|
||||||
base32_encode(re.data(), re.size(), digest.constData(), hostnameDigestSize);
|
|
||||||
|
|
||||||
// Chop extra null byte
|
|
||||||
re.chop(1);
|
|
||||||
|
|
||||||
return QString::fromLatin1(re);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray CryptoKey::signData(const QByteArray &data) const
|
|
||||||
{
|
|
||||||
QByteArray digest(32, 0);
|
|
||||||
bool ok = SHA256(reinterpret_cast<const unsigned char*>(data.constData()), data.size(),
|
|
||||||
reinterpret_cast<unsigned char*>(digest.data())) != NULL;
|
|
||||||
if (!ok) {
|
|
||||||
qWarning() << "Digest for RSA signature failed";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return signSHA256(digest);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray CryptoKey::signSHA256(const QByteArray &digest) const
|
|
||||||
{
|
|
||||||
if (!isPrivate())
|
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
QByteArray re(RSA_size(d->key), 0);
|
|
||||||
unsigned sigsize = 0;
|
|
||||||
int r = RSA_sign(NID_sha256, reinterpret_cast<const unsigned char*>(digest.constData()), digest.size(),
|
|
||||||
reinterpret_cast<unsigned char*>(re.data()), &sigsize, d->key);
|
|
||||||
|
|
||||||
if (r != 1) {
|
|
||||||
qWarning() << "RSA encryption failed when generating signature";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
re.truncate(sigsize);
|
|
||||||
return re;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CryptoKey::verifyData(const QByteArray &data, QByteArray signature) const
|
|
||||||
{
|
|
||||||
QByteArray digest(32, 0);
|
|
||||||
bool ok = SHA256(reinterpret_cast<const unsigned char*>(data.constData()), data.size(),
|
|
||||||
reinterpret_cast<unsigned char*>(digest.data())) != NULL;
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
qWarning() << "Digest for RSA verify failed";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return verifySHA256(digest, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CryptoKey::verifySHA256(const QByteArray &digest, QByteArray signature) const
|
|
||||||
{
|
|
||||||
if (!isLoaded())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int r = RSA_verify(NID_sha256, reinterpret_cast<const uchar*>(digest.constData()), digest.size(),
|
|
||||||
reinterpret_cast<uchar*>(signature.data()), signature.size(), d->key);
|
|
||||||
if (r != 1)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copyright (c) 2001-2004, Roger Dingledine
|
|
||||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
|
|
||||||
* Copyright (c) 2007-2010, The Tor Project, Inc.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
|
|
||||||
|
|
||||||
/* Implements base32 encoding as in rfc3548. Requires that srclen*8 is a multiple of 5. */
|
|
||||||
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen)
|
|
||||||
{
|
|
||||||
unsigned i, bit, v, u;
|
|
||||||
unsigned nbits = srclen * 8;
|
|
||||||
|
|
||||||
/* We need an even multiple of 5 bits, and enough space */
|
|
||||||
if ((nbits%5) != 0 || destlen > (nbits/5)+1) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
memset(dest, 0, destlen);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0, bit = 0; bit < nbits; ++i, bit += 5)
|
|
||||||
{
|
|
||||||
/* set v to the 16-bit value starting at src[bits/8], 0-padded. */
|
|
||||||
v = ((quint8) src[bit / 8]) << 8;
|
|
||||||
if (bit + 5 < nbits)
|
|
||||||
v += (quint8) src[(bit/8)+1];
|
|
||||||
|
|
||||||
/* set u to the 5-bit value at the bit'th bit of src. */
|
|
||||||
u = (v >> (11 - (bit % 8))) & 0x1F;
|
|
||||||
dest[i] = BASE32_CHARS[u];
|
|
||||||
}
|
|
||||||
|
|
||||||
dest[i] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Implements base32 decoding as in rfc3548. Requires that srclen*5 is a multiple of 8. */
|
|
||||||
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen)
|
|
||||||
{
|
|
||||||
unsigned int i, j, bit;
|
|
||||||
unsigned nbits = srclen * 5;
|
|
||||||
|
|
||||||
/* We need an even multiple of 8 bits, and enough space */
|
|
||||||
if ((nbits%8) != 0 || (nbits/8)+1 > destlen) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *tmp = new char[srclen];
|
|
||||||
|
|
||||||
/* Convert base32 encoded chars to the 5-bit values that they represent. */
|
|
||||||
for (j = 0; j < srclen; ++j)
|
|
||||||
{
|
|
||||||
if (src[j] > 0x60 && src[j] < 0x7B)
|
|
||||||
tmp[j] = src[j] - 0x61;
|
|
||||||
else if (src[j] > 0x31 && src[j] < 0x38)
|
|
||||||
tmp[j] = src[j] - 0x18;
|
|
||||||
else if (src[j] > 0x40 && src[j] < 0x5B)
|
|
||||||
tmp[j] = src[j] - 0x41;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete[] tmp;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assemble result byte-wise by applying five possible cases. */
|
|
||||||
for (i = 0, bit = 0; bit < nbits; ++i, bit += 8)
|
|
||||||
{
|
|
||||||
switch (bit % 40)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
dest[i] = (((quint8)tmp[(bit/5)]) << 3) + (((quint8)tmp[(bit/5)+1]) >> 2);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
dest[i] = (((quint8)tmp[(bit/5)]) << 6) + (((quint8)tmp[(bit/5)+1]) << 1)
|
|
||||||
+ (((quint8)tmp[(bit/5)+2]) >> 4);
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
dest[i] = (((quint8)tmp[(bit/5)]) << 4) + (((quint8)tmp[(bit/5)+1]) >> 1);
|
|
||||||
break;
|
|
||||||
case 24:
|
|
||||||
dest[i] = (((quint8)tmp[(bit/5)]) << 7) + (((quint8)tmp[(bit/5)+1]) << 2)
|
|
||||||
+ (((quint8)tmp[(bit/5)+2]) >> 3);
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
dest[i] = (((quint8)tmp[(bit/5)]) << 5) + ((quint8)tmp[(bit/5)+1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] tmp;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CRYPTOKEY_H
|
|
||||||
#define CRYPTOKEY_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QSharedData>
|
|
||||||
#include <QExplicitlySharedDataPointer>
|
|
||||||
|
|
||||||
class CryptoKey
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum KeyType {
|
|
||||||
PrivateKey,
|
|
||||||
PublicKey
|
|
||||||
};
|
|
||||||
|
|
||||||
enum KeyFormat {
|
|
||||||
PEM,
|
|
||||||
DER
|
|
||||||
};
|
|
||||||
|
|
||||||
CryptoKey();
|
|
||||||
~CryptoKey();
|
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM);
|
|
||||||
bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM);
|
|
||||||
#endif
|
|
||||||
bool loadFromFile(const QString &path);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
const QByteArray bytes() const { return key_data; }
|
|
||||||
bool loadFromTorMessage(const QByteArray& b);
|
|
||||||
bool isLoaded() const { return !key_data.isNull(); }
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
bool isPrivate() const;
|
|
||||||
|
|
||||||
QByteArray publicKeyDigest() const;
|
|
||||||
QByteArray encodedPublicKey(KeyFormat format) const;
|
|
||||||
QByteArray encodedPrivateKey(KeyFormat format) const;
|
|
||||||
QString torServiceID() const;
|
|
||||||
int bits() const;
|
|
||||||
|
|
||||||
// Calculate and sign SHA-256 digest of data using this key and PKCS #1 v2.0 padding
|
|
||||||
QByteArray signData(const QByteArray &data) const;
|
|
||||||
// Verify a signature as per signData
|
|
||||||
bool verifyData(const QByteArray &data, QByteArray signature) const;
|
|
||||||
|
|
||||||
// Sign the input SHA-256 digest using this key and PKCS #1 v2.0 padding
|
|
||||||
QByteArray signSHA256(const QByteArray &digest) const;
|
|
||||||
// Verify a signature as per signSHA256
|
|
||||||
bool verifySHA256(const QByteArray &digest, QByteArray signature) const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
struct Data : public QSharedData
|
|
||||||
{
|
|
||||||
typedef struct rsa_st RSA;
|
|
||||||
RSA *key;
|
|
||||||
|
|
||||||
Data(RSA *k = 0) : key(k) { }
|
|
||||||
~Data();
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QByteArray key_data;
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
QExplicitlySharedDataPointer<Data> d;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
QByteArray torControlHashedPassword(const QByteArray &password);
|
|
||||||
|
|
||||||
#endif // CRYPTOKEY_H
|
|
@ -1,147 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SecureRNG.h"
|
|
||||||
#include <QtDebug>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <wtypes.h>
|
|
||||||
#include <wincrypt.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if QT_VERSION >= 0x040700
|
|
||||||
#include <QElapsedTimer>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool SecureRNG::seed()
|
|
||||||
{
|
|
||||||
#if QT_VERSION >= 0x040700
|
|
||||||
QElapsedTimer timer;
|
|
||||||
timer.start();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
/* RAND_poll is very unreliable on windows; with older versions of OpenSSL,
|
|
||||||
* it can take up to several minutes to run and has been known to crash.
|
|
||||||
* Even newer versions seem to take around 400ms, which is far too long for
|
|
||||||
* interactive startup. Random data from the windows CSP is used as a seed
|
|
||||||
* instead, as it should be very high quality random and fast. */
|
|
||||||
HCRYPTPROV provider = 0;
|
|
||||||
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
|
||||||
{
|
|
||||||
qWarning() << "Failed to acquire CSP context for RNG seed:" << hex << GetLastError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Same amount of entropy OpenSSL uses, apparently. */
|
|
||||||
char buf[32];
|
|
||||||
|
|
||||||
if (!CryptGenRandom(provider, sizeof(buf), reinterpret_cast<BYTE*>(buf)))
|
|
||||||
{
|
|
||||||
qWarning() << "Failed to get entropy from CSP for RNG seed: " << hex << GetLastError();
|
|
||||||
CryptReleaseContext(provider, 0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptReleaseContext(provider, 0);
|
|
||||||
|
|
||||||
RAND_seed(buf, sizeof(buf));
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
#else
|
|
||||||
if (!RAND_poll())
|
|
||||||
{
|
|
||||||
qWarning() << "OpenSSL RNG seed failed:" << ERR_get_error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if QT_VERSION >= 0x040700
|
|
||||||
qDebug() << "RNG seed took" << timer.elapsed() << "ms";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SecureRNG::random(char *buf, int size)
|
|
||||||
{
|
|
||||||
int r = RAND_bytes(reinterpret_cast<unsigned char*>(buf), size);
|
|
||||||
if (r <= 0)
|
|
||||||
qFatal("RNG failed: %lu", ERR_get_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SecureRNG::random(int size)
|
|
||||||
{
|
|
||||||
QByteArray re(size, 0);
|
|
||||||
random(re.data(), size);
|
|
||||||
return re;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SecureRNG::randomPrintable(int length)
|
|
||||||
{
|
|
||||||
QByteArray re(length, 0);
|
|
||||||
for (int i = 0; i < re.size(); i++)
|
|
||||||
re[i] = randomInt(95) + 32;
|
|
||||||
return re;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned SecureRNG::randomInt(unsigned max)
|
|
||||||
{
|
|
||||||
unsigned cutoff = UINT_MAX - (UINT_MAX % max);
|
|
||||||
unsigned value = 0;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
random(reinterpret_cast<char*>(&value), sizeof(value));
|
|
||||||
if (value < cutoff)
|
|
||||||
return value % max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef UINT64_MAX
|
|
||||||
#define UINT64_MAX ((quint64)-1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
quint64 SecureRNG::randomInt64(quint64 max)
|
|
||||||
{
|
|
||||||
quint64 cutoff = UINT64_MAX - (UINT64_MAX % max);
|
|
||||||
quint64 value = 0;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
random(reinterpret_cast<char*>(value), sizeof(value));
|
|
||||||
if (value < cutoff)
|
|
||||||
return value % max;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,553 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Settings.h"
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonParseError>
|
|
||||||
#include <QSaveFile>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QPointer>
|
|
||||||
|
|
||||||
class SettingsFilePrivate : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
SettingsFile *q;
|
|
||||||
QString filePath;
|
|
||||||
QString errorMessage;
|
|
||||||
QTimer syncTimer;
|
|
||||||
QJsonObject jsonRoot;
|
|
||||||
SettingsObject *rootObject;
|
|
||||||
|
|
||||||
SettingsFilePrivate(SettingsFile *qp);
|
|
||||||
virtual ~SettingsFilePrivate();
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
void setError(const QString &message);
|
|
||||||
bool checkDirPermissions(const QString &path);
|
|
||||||
bool readFile();
|
|
||||||
bool writeFile();
|
|
||||||
|
|
||||||
static QStringList splitPath(const QString &input, bool &ok);
|
|
||||||
QJsonValue read(const QJsonObject &base, const QStringList &path);
|
|
||||||
bool write(const QStringList &path, const QJsonValue &value);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void modified(const QStringList &path, const QJsonValue &value);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void sync();
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsFile::SettingsFile(QObject *parent)
|
|
||||||
: QObject(parent), d(new SettingsFilePrivate(this))
|
|
||||||
{
|
|
||||||
d->rootObject = new SettingsObject(this, QString());
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsFile::~SettingsFile()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsFilePrivate::SettingsFilePrivate(SettingsFile *qp)
|
|
||||||
: QObject(qp)
|
|
||||||
, q(qp)
|
|
||||||
, rootObject(0)
|
|
||||||
{
|
|
||||||
syncTimer.setInterval(0);
|
|
||||||
syncTimer.setSingleShot(true);
|
|
||||||
connect(&syncTimer, &QTimer::timeout, this, &SettingsFilePrivate::sync);
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsFilePrivate::~SettingsFilePrivate()
|
|
||||||
{
|
|
||||||
if (syncTimer.isActive())
|
|
||||||
sync();
|
|
||||||
delete rootObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsFilePrivate::reset()
|
|
||||||
{
|
|
||||||
filePath.clear();
|
|
||||||
errorMessage.clear();
|
|
||||||
|
|
||||||
jsonRoot = QJsonObject();
|
|
||||||
emit modified(QStringList(), jsonRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SettingsFile::filePath() const
|
|
||||||
{
|
|
||||||
return d->filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsFile::setFilePath(const QString &filePath)
|
|
||||||
{
|
|
||||||
if (d->filePath == filePath)
|
|
||||||
return hasError();
|
|
||||||
|
|
||||||
d->reset();
|
|
||||||
d->filePath = filePath;
|
|
||||||
|
|
||||||
QFileInfo fileInfo(filePath);
|
|
||||||
QDir dir(fileInfo.path());
|
|
||||||
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
|
|
||||||
d->setError(QStringLiteral("Cannot create directory: %1").arg(dir.path()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d->checkDirPermissions(fileInfo.path());
|
|
||||||
|
|
||||||
if (!d->readFile())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SettingsFile::errorMessage() const
|
|
||||||
{
|
|
||||||
return d->errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsFile::hasError() const
|
|
||||||
{
|
|
||||||
return !d->errorMessage.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsFilePrivate::setError(const QString &message)
|
|
||||||
{
|
|
||||||
errorMessage = message;
|
|
||||||
emit q->error();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsFilePrivate::checkDirPermissions(const QString &path)
|
|
||||||
{
|
|
||||||
static QFile::Permissions desired = QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ExeUser;
|
|
||||||
static QFile::Permissions ignored = QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner;
|
|
||||||
|
|
||||||
QFile file(path);
|
|
||||||
if ((file.permissions() & ~ignored) != desired) {
|
|
||||||
qDebug() << "Correcting permissions on configuration directory";
|
|
||||||
if (!file.setPermissions(desired)) {
|
|
||||||
qWarning() << "Correcting permissions on configuration directory failed";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsObject *SettingsFile::root()
|
|
||||||
{
|
|
||||||
return d->rootObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SettingsObject *SettingsFile::root() const
|
|
||||||
{
|
|
||||||
return d->rootObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsFilePrivate::sync()
|
|
||||||
{
|
|
||||||
if (filePath.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
syncTimer.stop();
|
|
||||||
writeFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsFilePrivate::readFile()
|
|
||||||
{
|
|
||||||
QFile file(filePath);
|
|
||||||
if (!file.open(QIODevice::ReadWrite)) {
|
|
||||||
setError(file.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray data = file.readAll();
|
|
||||||
if (data.isEmpty() && (file.error() != QFileDevice::NoError || file.size() > 0)) {
|
|
||||||
setError(file.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
|
||||||
jsonRoot = QJsonObject();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError parseError;
|
|
||||||
QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
|
|
||||||
if (document.isNull()) {
|
|
||||||
setError(parseError.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!document.isObject()) {
|
|
||||||
setError(QStringLiteral("Invalid configuration file (expected object)"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonRoot = document.object();
|
|
||||||
|
|
||||||
emit modified(QStringList(), jsonRoot);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsFilePrivate::writeFile()
|
|
||||||
{
|
|
||||||
QSaveFile file(filePath);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
|
||||||
setError(file.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonDocument document(jsonRoot);
|
|
||||||
QByteArray data = document.toJson();
|
|
||||||
if (data.isEmpty() && !document.isEmpty()) {
|
|
||||||
setError(QStringLiteral("Encoding failure"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.write(data) < data.size() || !file.commit()) {
|
|
||||||
setError(file.errorString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList SettingsFilePrivate::splitPath(const QString &input, bool &ok)
|
|
||||||
{
|
|
||||||
QStringList components = input.split(QLatin1Char('.'));
|
|
||||||
|
|
||||||
// Allow a leading '.' to simplify concatenation
|
|
||||||
if (!components.isEmpty() && components.first().isEmpty())
|
|
||||||
components.takeFirst();
|
|
||||||
|
|
||||||
// No other empty components, including a trailing .
|
|
||||||
foreach (const QString &word, components) {
|
|
||||||
if (word.isEmpty()) {
|
|
||||||
ok = false;
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = true;
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonValue SettingsFilePrivate::read(const QJsonObject &base, const QStringList &path)
|
|
||||||
{
|
|
||||||
QJsonValue current = base;
|
|
||||||
|
|
||||||
foreach (const QString &key, path) {
|
|
||||||
QJsonObject object = current.toObject();
|
|
||||||
if (object.isEmpty() || (current = object.value(key)).isUndefined())
|
|
||||||
return QJsonValue::Undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare two QJsonValue to find keys that have changed,
|
|
||||||
// recursing into objects and building paths as necessary.
|
|
||||||
typedef QList<QPair<QStringList, QJsonValue> > ModifiedList;
|
|
||||||
static void findModifiedRecursive(ModifiedList &modified, const QStringList &path, const QJsonValue &oldValue, const QJsonValue &newValue)
|
|
||||||
{
|
|
||||||
if (oldValue.isObject() || newValue.isObject()) {
|
|
||||||
// If either is a non-object type, this returns an empty object
|
|
||||||
QJsonObject oldObject = oldValue.toObject();
|
|
||||||
QJsonObject newObject = newValue.toObject();
|
|
||||||
|
|
||||||
// Iterate keys of the original object and compare to new
|
|
||||||
for (QJsonObject::iterator it = oldObject.begin(); it != oldObject.end(); it++) {
|
|
||||||
QJsonValue newSubValue = newObject.value(it.key());
|
|
||||||
if (*it == newSubValue)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ((*it).isObject() || newSubValue.isObject())
|
|
||||||
findModifiedRecursive(modified, QStringList() << path << it.key(), *it, newSubValue);
|
|
||||||
else
|
|
||||||
modified.append(qMakePair(QStringList() << path << it.key(), newSubValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate keys of the new object that may not be in original
|
|
||||||
for (QJsonObject::iterator it = newObject.begin(); it != newObject.end(); it++) {
|
|
||||||
if (oldObject.contains(it.key()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ((*it).isObject())
|
|
||||||
findModifiedRecursive(modified, QStringList() << path << it.key(), QJsonValue::Undefined, it.value());
|
|
||||||
else
|
|
||||||
modified.append(qMakePair(QStringList() << path << it.key(), it.value()));
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
modified.append(qMakePair(path, newValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SettingsFilePrivate::write(const QStringList &path, const QJsonValue &value)
|
|
||||||
{
|
|
||||||
typedef QVarLengthArray<QPair<QString,QJsonObject> > ObjectStack;
|
|
||||||
ObjectStack stack;
|
|
||||||
QJsonValue current = jsonRoot;
|
|
||||||
QJsonValue originalValue;
|
|
||||||
QString currentKey;
|
|
||||||
|
|
||||||
foreach (const QString &key, path) {
|
|
||||||
const QJsonObject &parent = current.toObject();
|
|
||||||
stack.append(qMakePair(currentKey, parent));
|
|
||||||
current = parent.value(key);
|
|
||||||
currentKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack now contains parent objects starting with the root, and current
|
|
||||||
// is the old value. Write back changes in reverse.
|
|
||||||
if (current == value)
|
|
||||||
return false;
|
|
||||||
originalValue = current;
|
|
||||||
current = value;
|
|
||||||
|
|
||||||
ObjectStack::const_iterator it = stack.end(), begin = stack.begin();
|
|
||||||
while (it != begin) {
|
|
||||||
--it;
|
|
||||||
QJsonObject update = it->second;
|
|
||||||
update.insert(currentKey, current);
|
|
||||||
current = update;
|
|
||||||
currentKey = it->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// current is now the updated jsonRoot
|
|
||||||
jsonRoot = current.toObject();
|
|
||||||
syncTimer.start();
|
|
||||||
|
|
||||||
ModifiedList modified;
|
|
||||||
findModifiedRecursive(modified, path, originalValue, value);
|
|
||||||
|
|
||||||
for (ModifiedList::iterator it = modified.begin(); it != modified.end(); it++)
|
|
||||||
emit this->modified(it->first, it->second);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SettingsObjectPrivate : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SettingsObjectPrivate(SettingsObject *q);
|
|
||||||
|
|
||||||
SettingsObject *q;
|
|
||||||
SettingsFile *file;
|
|
||||||
QStringList path;
|
|
||||||
QJsonObject object;
|
|
||||||
bool invalid;
|
|
||||||
|
|
||||||
void setFile(SettingsFile *file);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void modified(const QStringList &absolutePath, const QJsonValue &value);
|
|
||||||
};
|
|
||||||
|
|
||||||
SettingsObject::SettingsObject(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, d(new SettingsObjectPrivate(this))
|
|
||||||
{
|
|
||||||
d->setFile(defaultFile());
|
|
||||||
if (d->file)
|
|
||||||
setPath(QString());
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsObject::SettingsObject(const QString &path, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, d(new SettingsObjectPrivate(this))
|
|
||||||
{
|
|
||||||
d->setFile(defaultFile());
|
|
||||||
setPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsObject::SettingsObject(SettingsFile *file, const QString &path, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, d(new SettingsObjectPrivate(this))
|
|
||||||
{
|
|
||||||
d->setFile(file);
|
|
||||||
setPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsObject::SettingsObject(SettingsObject *base, const QString &path, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, d(new SettingsObjectPrivate(this))
|
|
||||||
{
|
|
||||||
d->setFile(base->d->file);
|
|
||||||
setPath(base->path() + QLatin1Char('.') + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsObjectPrivate::SettingsObjectPrivate(SettingsObject *qp)
|
|
||||||
: QObject(qp)
|
|
||||||
, q(qp)
|
|
||||||
, file(0)
|
|
||||||
, invalid(true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObjectPrivate::setFile(SettingsFile *value)
|
|
||||||
{
|
|
||||||
if (file == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (file)
|
|
||||||
disconnect(file, 0, this, 0);
|
|
||||||
file = value;
|
|
||||||
if (file)
|
|
||||||
connect(file->d, &SettingsFilePrivate::modified, this, &SettingsObjectPrivate::modified);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit SettingsObject::modified with a relative path if path is matched
|
|
||||||
void SettingsObjectPrivate::modified(const QStringList &key, const QJsonValue &value)
|
|
||||||
{
|
|
||||||
if (key.size() < path.size())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < path.size(); i++) {
|
|
||||||
if (path[i] != key[i])
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
object = file->d->read(file->d->jsonRoot, path).toObject();
|
|
||||||
emit q->modified(QStringList(key.mid(path.size())).join(QLatin1Char('.')), value);
|
|
||||||
emit q->dataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QPointer<SettingsFile> defaultObjectFile;
|
|
||||||
|
|
||||||
SettingsFile *SettingsObject::defaultFile()
|
|
||||||
{
|
|
||||||
return defaultObjectFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObject::setDefaultFile(SettingsFile *file)
|
|
||||||
{
|
|
||||||
defaultObjectFile = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SettingsObject::path() const
|
|
||||||
{
|
|
||||||
return d->path.join(QLatin1Char('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObject::setPath(const QString &input)
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
QStringList newPath = SettingsFilePrivate::splitPath(input, ok);
|
|
||||||
if (!ok) {
|
|
||||||
d->invalid = true;
|
|
||||||
d->path.clear();
|
|
||||||
d->object = QJsonObject();
|
|
||||||
|
|
||||||
emit pathChanged();
|
|
||||||
emit dataChanged();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!d->invalid && d->path == newPath)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->path = newPath;
|
|
||||||
if (d->file) {
|
|
||||||
d->invalid = false;
|
|
||||||
d->object = d->file->d->read(d->file->d->jsonRoot, d->path).toObject();
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit pathChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject SettingsObject::data() const
|
|
||||||
{
|
|
||||||
return d->object;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObject::setData(const QJsonObject &input)
|
|
||||||
{
|
|
||||||
if (d->invalid || d->object == input)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->object = input;
|
|
||||||
d->file->d->write(d->path, d->object);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonValue SettingsObject::read(const QString &key, const QJsonValue &defaultValue) const
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
QStringList splitKey = SettingsFilePrivate::splitPath(key, ok);
|
|
||||||
if (d->invalid || !ok || splitKey.isEmpty()) {
|
|
||||||
qDebug() << "Invalid settings read of path" << key;
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonValue ret = d->file->d->read(d->object, splitKey);
|
|
||||||
if (ret.isUndefined())
|
|
||||||
ret = defaultValue;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObject::write(const QString &key, const QJsonValue &value)
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
QStringList splitKey = SettingsFilePrivate::splitPath(key, ok);
|
|
||||||
if (d->invalid || !ok || splitKey.isEmpty()) {
|
|
||||||
qDebug() << "Invalid settings write of path" << key;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
splitKey = d->path + splitKey;
|
|
||||||
d->file->d->write(splitKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObject::unset(const QString &key)
|
|
||||||
{
|
|
||||||
write(key, QJsonValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsObject::undefine()
|
|
||||||
{
|
|
||||||
if (d->invalid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->object = QJsonObject();
|
|
||||||
d->file->d->write(d->path, QJsonValue::Undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "Settings.moc"
|
|
@ -1,257 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SETTINGS_H
|
|
||||||
#define SETTINGS_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QJsonValue>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QDateTime>
|
|
||||||
|
|
||||||
class SettingsObject;
|
|
||||||
class SettingsFilePrivate;
|
|
||||||
class SettingsObjectPrivate;
|
|
||||||
|
|
||||||
/* SettingsFile represents a JSON-encoded configuration file.
|
|
||||||
*
|
|
||||||
* SettingsFile is an API for reading, writing, and change notification
|
|
||||||
* on JSON-encoded settings files.
|
|
||||||
*
|
|
||||||
* Data is accessed via SettingsObject, either using the root property
|
|
||||||
* or by creating a SettingsObject, optionally using a base path.
|
|
||||||
*/
|
|
||||||
class SettingsFile : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(SettingsFile)
|
|
||||||
|
|
||||||
Q_PROPERTY(SettingsObject *root READ root CONSTANT)
|
|
||||||
Q_PROPERTY(QString filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY error)
|
|
||||||
Q_PROPERTY(bool hasError READ hasError NOTIFY error)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SettingsFile(QObject *parent = 0);
|
|
||||||
virtual ~SettingsFile();
|
|
||||||
|
|
||||||
QString filePath() const;
|
|
||||||
bool setFilePath(const QString &filePath);
|
|
||||||
|
|
||||||
QString errorMessage() const;
|
|
||||||
bool hasError() const;
|
|
||||||
|
|
||||||
SettingsObject *root();
|
|
||||||
const SettingsObject *root() const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void filePathChanged();
|
|
||||||
void error();
|
|
||||||
|
|
||||||
private:
|
|
||||||
SettingsFilePrivate *d;
|
|
||||||
|
|
||||||
friend class SettingsObject;
|
|
||||||
friend class SettingsObjectPrivate;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* SettingsObject reads and writes data within a SettingsFile
|
|
||||||
*
|
|
||||||
* A SettingsObject is associated with a SettingsFile and represents an object
|
|
||||||
* tree within that file. It refers to the JSON object tree using a path
|
|
||||||
* notation with keys separated by '.'. For example:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* "one": {
|
|
||||||
* "two": {
|
|
||||||
* "three": "value"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* With this data, a SettingsObject with an empty path can read with the path
|
|
||||||
* "one.two.three", and a SettingsObject with a path of "one.two" can simply
|
|
||||||
* read or write on "three".
|
|
||||||
*
|
|
||||||
* Multiple SettingsObjects may be created for the same path, and will be kept
|
|
||||||
* synchronized with changes. The modified signal is emitted for all changes
|
|
||||||
* affecting keys within a path, including writes of object trees and from other
|
|
||||||
* instances.
|
|
||||||
*/
|
|
||||||
class SettingsObject : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(SettingsObject)
|
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
|
||||||
Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SettingsObject(QObject *parent = 0);
|
|
||||||
explicit SettingsObject(const QString &path, QObject *parent = 0);
|
|
||||||
explicit SettingsObject(SettingsFile *file, const QString &path, QObject *parent = 0);
|
|
||||||
explicit SettingsObject(SettingsObject *base, const QString &path, QObject *parent = 0);
|
|
||||||
|
|
||||||
/* Specify a SettingsFile to use by default on SettingsObject instances.
|
|
||||||
*
|
|
||||||
* After calling setDefaultFile, a SettingsObject created without any file, e.g.:
|
|
||||||
*
|
|
||||||
* SettingsObject settings;
|
|
||||||
* SettingsObject animals(QStringLiteral("animals"));
|
|
||||||
*
|
|
||||||
* Will use the specified SettingsFile instance by default. This is a convenience
|
|
||||||
* over passing around instances of SettingsFile in application use cases, and is
|
|
||||||
* particularly useful for QML.
|
|
||||||
*/
|
|
||||||
static SettingsFile *defaultFile();
|
|
||||||
static void setDefaultFile(SettingsFile *file);
|
|
||||||
|
|
||||||
QString path() const;
|
|
||||||
void setPath(const QString &path);
|
|
||||||
|
|
||||||
QJsonObject data() const;
|
|
||||||
void setData(const QJsonObject &data);
|
|
||||||
|
|
||||||
Q_INVOKABLE QJsonValue read(const QString &key, const QJsonValue &defaultValue = QJsonValue::Undefined) const;
|
|
||||||
template<typename T> T read(const QString &key) const;
|
|
||||||
Q_INVOKABLE void write(const QString &key, const QJsonValue &value);
|
|
||||||
template<typename T> void write(const QString &key, const T &value);
|
|
||||||
Q_INVOKABLE void unset(const QString &key);
|
|
||||||
|
|
||||||
// const char* key overloads
|
|
||||||
QJsonValue read(const char *key, const QJsonValue &defaultValue = QJsonValue::Undefined) const
|
|
||||||
{
|
|
||||||
return read(QString::fromLatin1(key), defaultValue);
|
|
||||||
}
|
|
||||||
template<typename T> T read(const char *key) const
|
|
||||||
{
|
|
||||||
return read<T>(QString::fromLatin1(key));
|
|
||||||
}
|
|
||||||
void write(const char *key, const QJsonValue &value)
|
|
||||||
{
|
|
||||||
write(QString::fromLatin1(key), value);
|
|
||||||
}
|
|
||||||
template<typename T> void write(const char *key, const T &value)
|
|
||||||
{
|
|
||||||
write<T>(QString::fromLatin1(key), value);
|
|
||||||
}
|
|
||||||
void unset(const char *key)
|
|
||||||
{
|
|
||||||
unset(QString::fromLatin1(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_INVOKABLE void undefine();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void pathChanged();
|
|
||||||
void dataChanged();
|
|
||||||
|
|
||||||
void modified(const QString &path, const QJsonValue &value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
SettingsObjectPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> inline void SettingsObject::write(const QString &key, const T &value)
|
|
||||||
{
|
|
||||||
write(key, QJsonValue(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline QString SettingsObject::read<QString>(const QString &key) const
|
|
||||||
{
|
|
||||||
return read(key).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline QJsonArray SettingsObject::read<QJsonArray>(const QString &key) const
|
|
||||||
{
|
|
||||||
return read(key).toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline QJsonObject SettingsObject::read<QJsonObject>(const QString &key) const
|
|
||||||
{
|
|
||||||
return read(key).toObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline double SettingsObject::read<double>(const QString &key) const
|
|
||||||
{
|
|
||||||
return read(key).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline int SettingsObject::read<int>(const QString &key) const
|
|
||||||
{
|
|
||||||
return read(key).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline bool SettingsObject::read<bool>(const QString &key) const
|
|
||||||
{
|
|
||||||
return read(key).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline QDateTime SettingsObject::read<QDateTime>(const QString &key) const
|
|
||||||
{
|
|
||||||
QString value = read(key).toString();
|
|
||||||
if (value.isEmpty())
|
|
||||||
return QDateTime();
|
|
||||||
return QDateTime::fromString(value, Qt::ISODate).toLocalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline void SettingsObject::write<QDateTime>(const QString &key, const QDateTime &value)
|
|
||||||
{
|
|
||||||
write(key, QJsonValue(value.toUTC().toString(Qt::ISODate)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicitly store value encoded as base64. Decodes and casts implicitly to QByteArray for reads.
|
|
||||||
class Base64Encode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Base64Encode(const QByteArray &value) : d(value) { }
|
|
||||||
operator QByteArray() { return d; }
|
|
||||||
QByteArray encoded() const { return d.toBase64(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
QByteArray d;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> inline Base64Encode SettingsObject::read<Base64Encode>(const QString &key) const
|
|
||||||
{
|
|
||||||
return Base64Encode(QByteArray::fromBase64(read(key).toString().toLatin1()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> inline void SettingsObject::write<Base64Encode>(const QString &key, const Base64Encode &value)
|
|
||||||
{
|
|
||||||
write(key, QJsonValue(QString::fromLatin1(value.encoded())));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,788 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "TorControl.h"
|
|
||||||
#include "TorControlSocket.h"
|
|
||||||
#include "HiddenService.h"
|
|
||||||
#include "ProtocolInfoCommand.h"
|
|
||||||
#include "AuthenticateCommand.h"
|
|
||||||
#include "SetConfCommand.h"
|
|
||||||
#include "GetConfCommand.h"
|
|
||||||
#include "AddOnionCommand.h"
|
|
||||||
#include "StrUtil.h"
|
|
||||||
#include "Settings.h"
|
|
||||||
#include "PendingOperation.h"
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QNetworkProxy>
|
|
||||||
//#include <QQmlEngine>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QSaveFile>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
Tor::TorControl *torControl = 0;
|
|
||||||
|
|
||||||
class nullstream: public std::ostream {};
|
|
||||||
|
|
||||||
static std::ostream& torctrldebug()
|
|
||||||
{
|
|
||||||
static nullstream null ;
|
|
||||||
|
|
||||||
if(true)
|
|
||||||
return std::cerr << time(NULL) << ":TOR CONTROL: " ;
|
|
||||||
else
|
|
||||||
return null ;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define torCtrlDebug torctrldebug
|
|
||||||
|
|
||||||
|
|
||||||
using namespace Tor;
|
|
||||||
|
|
||||||
namespace Tor {
|
|
||||||
|
|
||||||
class TorControlPrivate : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
TorControl *q;
|
|
||||||
|
|
||||||
TorControlSocket *socket;
|
|
||||||
QHostAddress torAddress;
|
|
||||||
QString errorMessage;
|
|
||||||
QString torVersion;
|
|
||||||
QByteArray authPassword;
|
|
||||||
QHostAddress socksAddress;
|
|
||||||
QList<HiddenService*> services;
|
|
||||||
quint16 controlPort, socksPort;
|
|
||||||
TorControl::Status status;
|
|
||||||
TorControl::TorStatus torStatus;
|
|
||||||
QVariantMap bootstrapStatus;
|
|
||||||
bool hasOwnership;
|
|
||||||
|
|
||||||
TorControlPrivate(TorControl *parent);
|
|
||||||
|
|
||||||
void setStatus(TorControl::Status status);
|
|
||||||
void setTorStatus(TorControl::TorStatus status);
|
|
||||||
|
|
||||||
void getTorInfo();
|
|
||||||
void publishServices();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void socketConnected();
|
|
||||||
void socketDisconnected();
|
|
||||||
void socketError();
|
|
||||||
|
|
||||||
void authenticateReply();
|
|
||||||
void protocolInfoReply();
|
|
||||||
void getTorInfoReply();
|
|
||||||
void setError(const QString &message);
|
|
||||||
|
|
||||||
void statusEvent(int code, const QByteArray &data);
|
|
||||||
void updateBootstrap(const QList<QByteArray> &data);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TorControl::TorControl(QObject *parent)
|
|
||||||
: QObject(parent), d(new TorControlPrivate(this))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TorControlPrivate::TorControlPrivate(TorControl *parent)
|
|
||||||
: QObject(parent), q(parent), controlPort(0), socksPort(0),
|
|
||||||
status(TorControl::NotConnected), torStatus(TorControl::TorUnknown),
|
|
||||||
hasOwnership(false)
|
|
||||||
{
|
|
||||||
socket = new TorControlSocket(this);
|
|
||||||
QObject::connect(socket, SIGNAL(connected()), this, SLOT(socketConnected()));
|
|
||||||
QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
|
||||||
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError()));
|
|
||||||
QObject::connect(socket, SIGNAL(error(QString)), this, SLOT(setError(QString)));
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkProxy TorControl::connectionProxy()
|
|
||||||
{
|
|
||||||
return QNetworkProxy(QNetworkProxy::Socks5Proxy, d->socksAddress.toString(), d->socksPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::setStatus(TorControl::Status n)
|
|
||||||
{
|
|
||||||
if (n == status)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TorControl::Status old = status;
|
|
||||||
status = n;
|
|
||||||
|
|
||||||
if (old == TorControl::Error)
|
|
||||||
errorMessage.clear();
|
|
||||||
|
|
||||||
emit q->statusChanged(status, old);
|
|
||||||
|
|
||||||
if (status == TorControl::Connected && old < TorControl::Connected)
|
|
||||||
emit q->connected();
|
|
||||||
else if (status < TorControl::Connected && old >= TorControl::Connected)
|
|
||||||
emit q->disconnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::setTorStatus(TorControl::TorStatus n)
|
|
||||||
{
|
|
||||||
if (n == torStatus)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TorControl::TorStatus old = torStatus;
|
|
||||||
torStatus = n;
|
|
||||||
emit q->torStatusChanged(torStatus, old);
|
|
||||||
emit q->connectivityChanged();
|
|
||||||
|
|
||||||
if (torStatus == TorControl::TorReady && socksAddress.isNull()) {
|
|
||||||
// Request info again to read the SOCKS port
|
|
||||||
getTorInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::setError(const QString &message)
|
|
||||||
{
|
|
||||||
errorMessage = message;
|
|
||||||
setStatus(TorControl::Error);
|
|
||||||
|
|
||||||
qWarning() << "torctrl: Error:" << errorMessage;
|
|
||||||
|
|
||||||
socket->abort();
|
|
||||||
|
|
||||||
QTimer::singleShot(15000, q, SLOT(reconnect()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TorControl::Status TorControl::status() const
|
|
||||||
{
|
|
||||||
return d->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorControl::TorStatus TorControl::torStatus() const
|
|
||||||
{
|
|
||||||
return d->torStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorControl::torVersion() const
|
|
||||||
{
|
|
||||||
return d->torVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorControl::errorMessage() const
|
|
||||||
{
|
|
||||||
return d->errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorControl::hasConnectivity() const
|
|
||||||
{
|
|
||||||
return torStatus() == TorReady && !d->socksAddress.isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHostAddress TorControl::socksAddress() const
|
|
||||||
{
|
|
||||||
return d->socksAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint16 TorControl::socksPort() const
|
|
||||||
{
|
|
||||||
return d->socksPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<HiddenService*> TorControl::hiddenServices() const
|
|
||||||
{
|
|
||||||
return d->services;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariantMap TorControl::bootstrapStatus() const
|
|
||||||
{
|
|
||||||
return d->bootstrapStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::setAuthPassword(const QByteArray &password)
|
|
||||||
{
|
|
||||||
d->authPassword = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::connect(const QHostAddress &address, quint16 port)
|
|
||||||
{
|
|
||||||
if (status() > Connecting)
|
|
||||||
{
|
|
||||||
torCtrlDebug() << "Ignoring TorControl::connect due to existing connection" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->torAddress = address;
|
|
||||||
d->controlPort = port;
|
|
||||||
d->setTorStatus(TorUnknown);
|
|
||||||
|
|
||||||
bool b = d->socket->blockSignals(true);
|
|
||||||
d->socket->abort();
|
|
||||||
d->socket->blockSignals(b);
|
|
||||||
|
|
||||||
d->setStatus(Connecting);
|
|
||||||
d->socket->connectToHost(address, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::reconnect()
|
|
||||||
{
|
|
||||||
Q_ASSERT(!d->torAddress.isNull() && d->controlPort);
|
|
||||||
if (d->torAddress.isNull() || !d->controlPort || status() >= Connecting)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->setStatus(Connecting);
|
|
||||||
d->socket->connectToHost(d->torAddress, d->controlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::authenticateReply()
|
|
||||||
{
|
|
||||||
AuthenticateCommand *command = qobject_cast<AuthenticateCommand*>(sender());
|
|
||||||
Q_ASSERT(command);
|
|
||||||
Q_ASSERT(status == TorControl::Authenticating);
|
|
||||||
if (!command)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!command->isSuccessful()) {
|
|
||||||
setError(command->errorMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
torCtrlDebug() << "torctrl: Authentication successful" << std::endl;
|
|
||||||
setStatus(TorControl::Connected);
|
|
||||||
|
|
||||||
setTorStatus(TorControl::TorUnknown);
|
|
||||||
|
|
||||||
TorControlCommand *clientEvents = new TorControlCommand;
|
|
||||||
connect(clientEvents, &TorControlCommand::replyLine, this, &TorControlPrivate::statusEvent);
|
|
||||||
socket->registerEvent("STATUS_CLIENT", clientEvents);
|
|
||||||
|
|
||||||
getTorInfo();
|
|
||||||
publishServices();
|
|
||||||
|
|
||||||
// XXX Fix old configurations that would store unwanted options in torrc.
|
|
||||||
// This can be removed some suitable amount of time after 1.0.4.
|
|
||||||
if (hasOwnership)
|
|
||||||
q->saveConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void TorControlPrivate::socketConnected()
|
|
||||||
{
|
|
||||||
Q_ASSERT(status == TorControl::Connecting);
|
|
||||||
|
|
||||||
torCtrlDebug() << "torctrl: Connected socket; querying information" << std::endl;
|
|
||||||
setStatus(TorControl::Authenticating);
|
|
||||||
|
|
||||||
ProtocolInfoCommand *command = new ProtocolInfoCommand(q);
|
|
||||||
connect(command, &TorControlCommand::finished, this, &TorControlPrivate::protocolInfoReply);
|
|
||||||
socket->sendCommand(command, command->build());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::socketDisconnected()
|
|
||||||
{
|
|
||||||
/* Clear some internal state */
|
|
||||||
torVersion.clear();
|
|
||||||
socksAddress.clear();
|
|
||||||
socksPort = 0;
|
|
||||||
setTorStatus(TorControl::TorUnknown);
|
|
||||||
|
|
||||||
/* This emits the disconnected() signal as well */
|
|
||||||
setStatus(TorControl::NotConnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::socketError()
|
|
||||||
{
|
|
||||||
setError(QStringLiteral("Connection failed: %1").arg(socket->errorString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::protocolInfoReply()
|
|
||||||
{
|
|
||||||
ProtocolInfoCommand *info = qobject_cast<ProtocolInfoCommand*>(sender());
|
|
||||||
if (!info)
|
|
||||||
return;
|
|
||||||
|
|
||||||
torVersion = info->torVersion();
|
|
||||||
|
|
||||||
if (status == TorControl::Authenticating)
|
|
||||||
{
|
|
||||||
AuthenticateCommand *auth = new AuthenticateCommand;
|
|
||||||
connect(auth, &TorControlCommand::finished, this, &TorControlPrivate::authenticateReply);
|
|
||||||
|
|
||||||
QByteArray data;
|
|
||||||
ProtocolInfoCommand::AuthMethods methods = info->authMethods();
|
|
||||||
|
|
||||||
if (methods.testFlag(ProtocolInfoCommand::AuthNull))
|
|
||||||
{
|
|
||||||
torCtrlDebug() << "torctrl: Using null authentication" << std::endl;
|
|
||||||
data = auth->build();
|
|
||||||
}
|
|
||||||
else if (methods.testFlag(ProtocolInfoCommand::AuthCookie) && !info->cookieFile().isEmpty())
|
|
||||||
{
|
|
||||||
QString cookieFile = info->cookieFile();
|
|
||||||
QString cookieError;
|
|
||||||
torCtrlDebug() << "torctrl: Using cookie authentication with file" << cookieFile.toStdString() << std::endl;
|
|
||||||
|
|
||||||
QFile file(cookieFile);
|
|
||||||
if (file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
QByteArray cookie = file.readAll();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
/* Simple test to avoid a vulnerability where any process listening on what we think is
|
|
||||||
* the control port could trick us into sending the contents of an arbitrary file */
|
|
||||||
if (cookie.size() == 32)
|
|
||||||
data = auth->build(cookie);
|
|
||||||
else
|
|
||||||
cookieError = QStringLiteral("Unexpected file size");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cookieError = file.errorString();
|
|
||||||
|
|
||||||
if (!cookieError.isNull() || data.isNull())
|
|
||||||
{
|
|
||||||
/* If we know a password and password authentication is allowed, try using that instead.
|
|
||||||
* This is a strange corner case that will likely never happen in a normal configuration,
|
|
||||||
* but it has happened. */
|
|
||||||
if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword) && !authPassword.isEmpty())
|
|
||||||
{
|
|
||||||
torCtrlDebug() << "torctrl: Unable to read authentication cookie file:" << cookieError.toStdString() << std::endl;
|
|
||||||
goto usePasswordAuth;
|
|
||||||
}
|
|
||||||
|
|
||||||
setError(QStringLiteral("Unable to read authentication cookie file: %1").arg(cookieError));
|
|
||||||
delete auth;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword) && !authPassword.isEmpty())
|
|
||||||
{
|
|
||||||
usePasswordAuth:
|
|
||||||
torCtrlDebug() << "torctrl: Using hashed password authentication" << std::endl;
|
|
||||||
data = auth->build(authPassword);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword))
|
|
||||||
setError(QStringLiteral("Tor requires a control password to connect, but no password is configured."));
|
|
||||||
else
|
|
||||||
setError(QStringLiteral("Tor is not configured to accept any supported authentication methods."));
|
|
||||||
delete auth;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket->sendCommand(auth, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::getTorInfo()
|
|
||||||
{
|
|
||||||
Q_ASSERT(q->isConnected());
|
|
||||||
|
|
||||||
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetInfo);
|
|
||||||
connect(command, &TorControlCommand::finished, this, &TorControlPrivate::getTorInfoReply);
|
|
||||||
|
|
||||||
QList<QByteArray> keys;
|
|
||||||
keys << QByteArray("status/circuit-established") << QByteArray("status/bootstrap-phase");
|
|
||||||
|
|
||||||
/* If these are set in the config, they override the automatic behavior. */
|
|
||||||
SettingsObject settings(QStringLiteral("tor"));
|
|
||||||
QHostAddress forceAddress(settings.read("socksAddress").toString());
|
|
||||||
quint16 port = (quint16)settings.read("socksPort").toInt();
|
|
||||||
|
|
||||||
if (!forceAddress.isNull() && port) {
|
|
||||||
torCtrlDebug() << "torctrl: Using manually specified SOCKS connection settings";
|
|
||||||
socksAddress = forceAddress;
|
|
||||||
socksPort = port;
|
|
||||||
emit q->connectivityChanged();
|
|
||||||
} else
|
|
||||||
keys << QByteArray("net/listeners/socks");
|
|
||||||
|
|
||||||
socket->sendCommand(command, command->build(keys));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::getTorInfoReply()
|
|
||||||
{
|
|
||||||
GetConfCommand *command = qobject_cast<GetConfCommand*>(sender());
|
|
||||||
if (!command || !q->isConnected())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QList<QByteArray> listenAddresses = splitQuotedStrings(command->get(QByteArray("net/listeners/socks")).toString().toLatin1(), ' ');
|
|
||||||
for (QList<QByteArray>::Iterator it = listenAddresses.begin(); it != listenAddresses.end(); ++it) {
|
|
||||||
QByteArray value = unquotedString(*it);
|
|
||||||
int sepp = value.indexOf(':');
|
|
||||||
QHostAddress address(QString::fromLatin1(value.mid(0, sepp)));
|
|
||||||
quint16 port = (quint16)value.mid(sepp+1).toUInt();
|
|
||||||
|
|
||||||
/* Use the first address that matches the one used for this control connection. If none do,
|
|
||||||
* just use the first address and rely on the user to reconfigure if necessary (not a problem;
|
|
||||||
* their setup is already very customized) */
|
|
||||||
if (socksAddress.isNull() || address == socket->peerAddress()) {
|
|
||||||
socksAddress = address;
|
|
||||||
socksPort = port;
|
|
||||||
if (address == socket->peerAddress())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* It is not immediately an error to have no SOCKS address; when DisableNetwork is set there won't be a
|
|
||||||
* listener yet. To handle that situation, we'll try to read the socks address again when TorReady state
|
|
||||||
* is reached. */
|
|
||||||
if (!socksAddress.isNull()) {
|
|
||||||
torCtrlDebug() << "torctrl: SOCKS address is " << socksAddress.toString().toStdString() << ":" << socksPort << std::endl;
|
|
||||||
emit q->connectivityChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command->get(QByteArray("status/circuit-established")).toInt() == 1) {
|
|
||||||
torCtrlDebug() << "torctrl: Tor indicates that circuits have been established; state is TorReady" << std::endl;
|
|
||||||
setTorStatus(TorControl::TorReady);
|
|
||||||
} else {
|
|
||||||
setTorStatus(TorControl::TorOffline);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray bootstrap = command->get(QByteArray("status/bootstrap-phase")).toString().toLatin1();
|
|
||||||
if (!bootstrap.isEmpty())
|
|
||||||
updateBootstrap(splitQuotedStrings(bootstrap, ' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::addHiddenService(HiddenService *service)
|
|
||||||
{
|
|
||||||
if (d->services.contains(service))
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->services.append(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::publishServices()
|
|
||||||
{
|
|
||||||
torCtrlDebug() << "Publish Services... " ;
|
|
||||||
|
|
||||||
Q_ASSERT(q->isConnected());
|
|
||||||
if (services.isEmpty())
|
|
||||||
{
|
|
||||||
std::cerr << "No service regstered!" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::cerr << std::endl;
|
|
||||||
|
|
||||||
SettingsObject settings(QStringLiteral("tor"));
|
|
||||||
if (settings.read("neverPublishServices").toBool())
|
|
||||||
{
|
|
||||||
torCtrlDebug() << "torctrl: Skipping service publication because neverPublishService is enabled" << std::endl;
|
|
||||||
|
|
||||||
/* Call servicePublished under the assumption that they're published externally. */
|
|
||||||
for (QList<HiddenService*>::Iterator it = services.begin(); it != services.end(); ++it)
|
|
||||||
(*it)->servicePublished();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (q->torVersionAsNewAs(QStringLiteral("0.2.7"))) {
|
|
||||||
foreach (HiddenService *service, services) {
|
|
||||||
if (service->hostname().isEmpty())
|
|
||||||
torCtrlDebug() << "torctrl: Creating a new hidden service" << std::endl;
|
|
||||||
else
|
|
||||||
torCtrlDebug() << "torctrl: Publishing hidden service: " << service->hostname().toStdString() << std::endl;
|
|
||||||
AddOnionCommand *onionCommand = new AddOnionCommand(service);
|
|
||||||
QObject::connect(onionCommand, &AddOnionCommand::succeeded, service, &HiddenService::servicePublished);
|
|
||||||
socket->sendCommand(onionCommand, onionCommand->build());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
torCtrlDebug() << "torctrl: Using legacy SETCONF hidden service configuration for tor" << torVersion.toStdString() << std::endl;
|
|
||||||
SetConfCommand *command = new SetConfCommand;
|
|
||||||
QList<QPair<QByteArray,QByteArray> > torConfig;
|
|
||||||
|
|
||||||
foreach (HiddenService *service, services)
|
|
||||||
{
|
|
||||||
if (service->dataPath().isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (service->privateKey().isLoaded() && !QFile::exists(service->dataPath() + QStringLiteral("/private_key"))) {
|
|
||||||
// This case can happen if tor is downgraded after the profile is created
|
|
||||||
qWarning() << "Cannot publish ephemeral hidden services with this version of tor; skipping";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
torCtrlDebug() << "torctrl: Configuring hidden service at" << service->dataPath().toStdString() << std::endl;
|
|
||||||
|
|
||||||
QDir dir(service->dataPath());
|
|
||||||
torConfig.append(qMakePair(QByteArray("HiddenServiceDir"), dir.absolutePath().toLocal8Bit()));
|
|
||||||
|
|
||||||
const QList<HiddenService::Target> &targets = service->targets();
|
|
||||||
for (QList<HiddenService::Target>::ConstIterator tit = targets.begin(); tit != targets.end(); ++tit)
|
|
||||||
{
|
|
||||||
QString target = QString::fromLatin1("%1 %2:%3").arg(tit->servicePort)
|
|
||||||
.arg(tit->targetAddress.toString())
|
|
||||||
.arg(tit->targetPort);
|
|
||||||
torConfig.append(qMakePair(QByteArray("HiddenServicePort"), target.toLatin1()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(command, &SetConfCommand::setConfSucceeded, service, &HiddenService::servicePublished);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!torConfig.isEmpty())
|
|
||||||
socket->sendCommand(command, command->build(torConfig));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::shutdown()
|
|
||||||
{
|
|
||||||
if (!hasOwnership()) {
|
|
||||||
qWarning() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->socket->sendCommand("SIGNAL SHUTDOWN\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::shutdownSync()
|
|
||||||
{
|
|
||||||
if (!hasOwnership()) {
|
|
||||||
qWarning() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown();
|
|
||||||
while (d->socket->bytesToWrite())
|
|
||||||
{
|
|
||||||
if (!d->socket->waitForBytesWritten(5000))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::statusEvent(int code, const QByteArray &data)
|
|
||||||
{
|
|
||||||
Q_UNUSED(code);
|
|
||||||
|
|
||||||
QList<QByteArray> tokens = splitQuotedStrings(data.trimmed(), ' ');
|
|
||||||
if (tokens.size() < 3)
|
|
||||||
return;
|
|
||||||
|
|
||||||
torCtrlDebug() << "torctrl: status event:" << QString(data.trimmed()).toStdString() << std::endl;
|
|
||||||
|
|
||||||
if (tokens[2] == "CIRCUIT_ESTABLISHED") {
|
|
||||||
setTorStatus(TorControl::TorReady);
|
|
||||||
} else if (tokens[2] == "CIRCUIT_NOT_ESTABLISHED") {
|
|
||||||
setTorStatus(TorControl::TorOffline);
|
|
||||||
} else if (tokens[2] == "BOOTSTRAP") {
|
|
||||||
tokens.takeFirst();
|
|
||||||
updateBootstrap(tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControlPrivate::updateBootstrap(const QList<QByteArray> &data)
|
|
||||||
{
|
|
||||||
bootstrapStatus.clear();
|
|
||||||
// WARN or NOTICE
|
|
||||||
bootstrapStatus[QStringLiteral("severity")] = data.value(0);
|
|
||||||
for (int i = 1; i < data.size(); i++) {
|
|
||||||
int equals = data[i].indexOf('=');
|
|
||||||
QString key = QString::fromLatin1(data[i].mid(0, equals));
|
|
||||||
QString value;
|
|
||||||
if (equals >= 0)
|
|
||||||
value = QString::fromLatin1(unquotedString(data[i].mid(equals + 1)));
|
|
||||||
bootstrapStatus[key.toLower()] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//torCtrlDebug() << bootstrapStatus << std::endl;
|
|
||||||
|
|
||||||
emit q->bootstrapStatusChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject *TorControl::getConfiguration(const QString &options)
|
|
||||||
{
|
|
||||||
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetConf);
|
|
||||||
d->socket->sendCommand(command, command->build(options.toLatin1()));
|
|
||||||
|
|
||||||
//QQmlEngine::setObjectOwnership(command, QQmlEngine::CppOwnership);
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject *TorControl::setConfiguration(const QVariantMap &options)
|
|
||||||
{
|
|
||||||
SetConfCommand *command = new SetConfCommand;
|
|
||||||
command->setResetMode(true);
|
|
||||||
d->socket->sendCommand(command, command->build(options));
|
|
||||||
|
|
||||||
//QQmlEngine::setObjectOwnership(command, QQmlEngine::CppOwnership);
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Tor {
|
|
||||||
|
|
||||||
class SaveConfigOperation : public PendingOperation
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
SaveConfigOperation(QObject *parent)
|
|
||||||
: PendingOperation(parent), command(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void start(TorControlSocket *socket)
|
|
||||||
{
|
|
||||||
Q_ASSERT(!command);
|
|
||||||
command = new GetConfCommand(GetConfCommand::GetInfo);
|
|
||||||
QObject::connect(command, &TorControlCommand::finished, this, &SaveConfigOperation::configTextReply);
|
|
||||||
socket->sendCommand(command, command->build(QList<QByteArray>() << "config-text" << "config-file"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void configTextReply()
|
|
||||||
{
|
|
||||||
Q_ASSERT(command);
|
|
||||||
if (!command)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString path = QFile::decodeName(command->get("config-file").toByteArray());
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
finishWithError(QStringLiteral("Cannot write torrc without knowing its path"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Out of paranoia, refuse to write any file not named 'torrc', or if the
|
|
||||||
// file doesn't exist
|
|
||||||
QFileInfo fileInfo(path);
|
|
||||||
if (fileInfo.fileName() != QStringLiteral("torrc") || !fileInfo.exists()) {
|
|
||||||
finishWithError(QStringLiteral("Refusing to write torrc to unacceptable path %1").arg(path));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSaveFile file(path);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
||||||
finishWithError(QStringLiteral("Failed opening torrc file for writing: %1").arg(file.errorString()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove these keys when writing torrc; they are set at runtime and contain
|
|
||||||
// absolute paths or port numbers
|
|
||||||
static const char *bannedKeys[] = {
|
|
||||||
"ControlPortWriteToFile",
|
|
||||||
"DataDirectory",
|
|
||||||
"HiddenServiceDir",
|
|
||||||
"HiddenServicePort",
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
QVariantList configText = command->get("config-text").toList();
|
|
||||||
foreach (const QVariant &value, configText) {
|
|
||||||
QByteArray line = value.toByteArray();
|
|
||||||
|
|
||||||
bool skip = false;
|
|
||||||
for (const char **key = bannedKeys; *key; key++) {
|
|
||||||
if (line.startsWith(*key)) {
|
|
||||||
skip = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (skip)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
file.write(line);
|
|
||||||
file.write("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.commit()) {
|
|
||||||
finishWithError(QStringLiteral("Failed writing torrc: %1").arg(file.errorString()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
torCtrlDebug() << "torctrl: Wrote torrc file" << std::endl;
|
|
||||||
finishWithSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
GetConfCommand *command;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
PendingOperation *TorControl::saveConfiguration()
|
|
||||||
{
|
|
||||||
if (!hasOwnership()) {
|
|
||||||
qWarning() << "torctrl: Ignoring save configuration command for a tor instance I don't own";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveConfigOperation *operation = new SaveConfigOperation(this);
|
|
||||||
QObject::connect(operation, &PendingOperation::finished, operation, &QObject::deleteLater);
|
|
||||||
operation->start(d->socket);
|
|
||||||
|
|
||||||
//QQmlEngine::setObjectOwnership(operation, QQmlEngine::CppOwnership);
|
|
||||||
return operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorControl::hasOwnership() const
|
|
||||||
{
|
|
||||||
return d->hasOwnership;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorControl::takeOwnership()
|
|
||||||
{
|
|
||||||
d->hasOwnership = true;
|
|
||||||
d->socket->sendCommand("TAKEOWNERSHIP\r\n");
|
|
||||||
|
|
||||||
// Reset PID-based polling
|
|
||||||
QVariantMap options;
|
|
||||||
options[QStringLiteral("__OwningControllerProcess")] = QVariant();
|
|
||||||
setConfiguration(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorControl::torVersionAsNewAs(const QString &match) const
|
|
||||||
{
|
|
||||||
QRegularExpression r(QStringLiteral("[.-]"));
|
|
||||||
QStringList split = torVersion().split(r);
|
|
||||||
QStringList matchSplit = match.split(r);
|
|
||||||
|
|
||||||
for (int i = 0; i < matchSplit.size(); i++) {
|
|
||||||
if (i >= split.size())
|
|
||||||
return false;
|
|
||||||
bool ok1 = false, ok2 = false;
|
|
||||||
int currentVal = split[i].toInt(&ok1);
|
|
||||||
int matchVal = matchSplit[i].toInt(&ok2);
|
|
||||||
if (!ok1 || !ok2)
|
|
||||||
return false;
|
|
||||||
if (currentVal > matchVal)
|
|
||||||
return true;
|
|
||||||
if (currentVal < matchVal)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Versions are equal, up to the length of match
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "TorControl.moc"
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TORCONTROL_H
|
|
||||||
#define TORCONTROL_H
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include "PendingOperation.h"
|
|
||||||
|
|
||||||
class QNetworkProxy;
|
|
||||||
|
|
||||||
namespace Tor
|
|
||||||
{
|
|
||||||
|
|
||||||
class HiddenService;
|
|
||||||
class TorControlPrivate;
|
|
||||||
|
|
||||||
class TorControl : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_ENUMS(Status TorStatus)
|
|
||||||
|
|
||||||
// Status of the control connection
|
|
||||||
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
|
||||||
// Status of Tor (and whether it believes it can connect)
|
|
||||||
Q_PROPERTY(TorStatus torStatus READ torStatus NOTIFY torStatusChanged)
|
|
||||||
// Whether it's possible to make a SOCKS connection and connect
|
|
||||||
Q_PROPERTY(bool hasConnectivity READ hasConnectivity NOTIFY connectivityChanged)
|
|
||||||
Q_PROPERTY(QString torVersion READ torVersion NOTIFY connected)
|
|
||||||
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY statusChanged)
|
|
||||||
Q_PROPERTY(QVariantMap bootstrapStatus READ bootstrapStatus NOTIFY bootstrapStatusChanged)
|
|
||||||
Q_PROPERTY(bool hasOwnership READ hasOwnership NOTIFY hasOwnershipChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum Status
|
|
||||||
{
|
|
||||||
Error = -1,
|
|
||||||
NotConnected = 0x00,
|
|
||||||
Connecting = 0x01,
|
|
||||||
Authenticating = 0x02,
|
|
||||||
Connected = 0x03
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TorStatus
|
|
||||||
{
|
|
||||||
TorUnknown = 0x00,
|
|
||||||
TorOffline = 0x01,
|
|
||||||
TorReady = 0x02
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
explicit TorControl(QObject *parent = 0);
|
|
||||||
|
|
||||||
/* Information */
|
|
||||||
Status status() const;
|
|
||||||
TorStatus torStatus() const;
|
|
||||||
QString torVersion() const;
|
|
||||||
bool torVersionAsNewAs(const QString &version) const;
|
|
||||||
QString errorMessage() const;
|
|
||||||
|
|
||||||
bool hasConnectivity() const;
|
|
||||||
QHostAddress socksAddress() const;
|
|
||||||
quint16 socksPort() const;
|
|
||||||
QNetworkProxy connectionProxy();
|
|
||||||
|
|
||||||
/* Authentication */
|
|
||||||
void setAuthPassword(const QByteArray &password);
|
|
||||||
|
|
||||||
/* Connection */
|
|
||||||
bool isConnected() const { return status() == Connected; }
|
|
||||||
void connect(const QHostAddress &address, quint16 port);
|
|
||||||
|
|
||||||
/* Ownership means that tor is managed by this socket, and we
|
|
||||||
* can shut it down, own its configuration, etc. */
|
|
||||||
bool hasOwnership() const;
|
|
||||||
void takeOwnership();
|
|
||||||
|
|
||||||
/* Hidden Services */
|
|
||||||
QList<HiddenService*> hiddenServices() const;
|
|
||||||
void addHiddenService(HiddenService *service);
|
|
||||||
|
|
||||||
QVariantMap bootstrapStatus() const;
|
|
||||||
Q_INVOKABLE QObject *getConfiguration(const QString &options);
|
|
||||||
Q_INVOKABLE QObject *setConfiguration(const QVariantMap &options);
|
|
||||||
Q_INVOKABLE PendingOperation *saveConfiguration();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void statusChanged(int newStatus, int oldStatus);
|
|
||||||
void torStatusChanged(int newStatus, int oldStatus);
|
|
||||||
void connected();
|
|
||||||
void disconnected();
|
|
||||||
void connectivityChanged();
|
|
||||||
void bootstrapStatusChanged();
|
|
||||||
void hasOwnershipChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
/* Instruct Tor to shutdown */
|
|
||||||
void shutdown();
|
|
||||||
/* Call shutdown(), and wait synchronously for the command to be written */
|
|
||||||
void shutdownSync();
|
|
||||||
|
|
||||||
void reconnect();
|
|
||||||
|
|
||||||
private:
|
|
||||||
TorControlPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extern Tor::TorControl *torControl;
|
|
||||||
|
|
||||||
#endif // TORCONTROLMANAGER_H
|
|
@ -9,42 +9,59 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "util/rstime.h"
|
#include "util/rstime.h"
|
||||||
|
|
||||||
|
#include "retroshare/rstor.h"
|
||||||
|
#include "retroshare/rsevents.h"
|
||||||
|
|
||||||
#include "TorControlWindow.h"
|
#include "TorControlWindow.h"
|
||||||
#include "TorManager.h"
|
#include "util/qtthreadsutils.h"
|
||||||
#include "TorControl.h"
|
|
||||||
#include "HiddenService.h"
|
|
||||||
|
|
||||||
TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent)
|
TorControlDialog::TorControlDialog(QWidget *)
|
||||||
: mTorManager(tm)
|
|
||||||
{
|
{
|
||||||
setupUi(this) ;
|
setupUi(this) ;
|
||||||
|
|
||||||
QObject::connect(tm->control(),SIGNAL(statusChanged(int,int)),this,SLOT(statusChanged())) ;
|
mEventHandlerId = 0; // very important!
|
||||||
QObject::connect(tm->control(),SIGNAL(connected()),this,SLOT(statusChanged()));
|
|
||||||
QObject::connect(tm->control(),SIGNAL(disconnected()),this,SLOT(statusChanged()));
|
|
||||||
QObject::connect(tm->control(),SIGNAL(bootstrapStatusChanged()),this,SLOT(statusChanged()));
|
|
||||||
QObject::connect(tm->control(),SIGNAL(connectivityChanged()),this,SLOT(statusChanged()));
|
|
||||||
QObject::connect(tm ,SIGNAL(errorChanged()),this,SLOT(statusChanged()));
|
|
||||||
|
|
||||||
//QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ;
|
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
|
||||||
|
{
|
||||||
|
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
|
||||||
|
}, mEventHandlerId, RsEventType::TOR_MANAGER );
|
||||||
|
|
||||||
mIncomingServer = new QTcpServer(this) ;
|
mIncomingServer = new QTcpServer(this) ;
|
||||||
mHiddenService = NULL ;
|
|
||||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN;
|
|
||||||
//mBootstrapPhaseFinished = false ;
|
|
||||||
|
|
||||||
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
|
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
|
||||||
|
|
||||||
QTimer *timer = new QTimer ;
|
QTimer *timer = new QTimer ;
|
||||||
|
|
||||||
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
|
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
|
||||||
timer->start(500) ;
|
timer->start(300) ;
|
||||||
|
|
||||||
// Hide some debug output for the released version
|
// Hide some debug output for the released version
|
||||||
|
|
||||||
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
|
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
|
||||||
|
|
||||||
adjustSize();
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlDialog::~TorControlDialog()
|
||||||
|
{
|
||||||
|
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||||
|
{
|
||||||
|
if(event->mType != RsEventType::TOR_MANAGER) return;
|
||||||
|
|
||||||
|
const RsTorManagerEvent *fe = dynamic_cast<const RsTorManagerEvent*>(event.get());
|
||||||
|
if(!fe)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(fe->mTorManagerEventType)
|
||||||
|
{
|
||||||
|
case RsTorManagerEventCode::TOR_STATUS_CHANGED:
|
||||||
|
case RsTorManagerEventCode::TOR_CONNECTIVITY_CHANGED: statusChanged(fe->mTorStatus,fe->mTorConnectivityStatus);
|
||||||
|
break;
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlDialog::onIncomingConnection()
|
void TorControlDialog::onIncomingConnection()
|
||||||
@ -52,67 +69,66 @@ void TorControlDialog::onIncomingConnection()
|
|||||||
std::cerr << "Incoming connection !!" << std::endl;
|
std::cerr << "Incoming connection !!" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlDialog::statusChanged()
|
void TorControlDialog::statusChanged(RsTorStatus torstatus, RsTorConnectivityStatus tor_control_status)
|
||||||
{
|
{
|
||||||
int tor_control_status = mTorManager->control()->status();
|
|
||||||
int torstatus = mTorManager->control()->torStatus();
|
|
||||||
|
|
||||||
QString tor_control_status_str,torstatus_str ;
|
QString tor_control_status_str,torstatus_str ;
|
||||||
|
|
||||||
if(mTorManager->hasError())
|
if(RsTor::hasError())
|
||||||
mErrorMsg = mTorManager->errorMessage() ;
|
mErrorMsg = QString::fromStdString(RsTor::errorMessage()) ;
|
||||||
|
|
||||||
switch(tor_control_status)
|
switch(tor_control_status)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case Tor::TorControl::Error : tor_control_status_str = "Error" ; break ;
|
case RsTorConnectivityStatus::ERROR: tor_control_status_str = tr("Error") ; break ;
|
||||||
case Tor::TorControl::NotConnected: tor_control_status_str = "Not connected" ; break ;
|
case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_status_str = tr("Not connected") ; break ;
|
||||||
case Tor::TorControl::Connecting: tor_control_status_str = "Connecting" ; break ;
|
case RsTorConnectivityStatus::CONNECTING: tor_control_status_str = tr("Connecting") ; break ;
|
||||||
case Tor::TorControl::Authenticating: tor_control_status_str = "Authenticating" ; break ;
|
case RsTorConnectivityStatus::SOCKET_CONNECTED: tor_control_status_str = tr("Socket connected") ; break ;
|
||||||
case Tor::TorControl::Connected: tor_control_status_str = "Connected" ; break ;
|
case RsTorConnectivityStatus::AUTHENTICATING: tor_control_status_str = tr("Authenticating") ; break ;
|
||||||
}
|
case RsTorConnectivityStatus::AUTHENTICATED: tor_control_status_str = tr("Authenticated") ; break ;
|
||||||
|
case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_status_str = tr("Hidden service ready") ; break ;
|
||||||
|
case RsTorConnectivityStatus::UNKNOWN: tor_control_status_str = tr("Unknown") ; break ;
|
||||||
|
}
|
||||||
|
|
||||||
switch(torstatus)
|
switch(torstatus)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
|
case RsTorStatus::UNKNOWN: torstatus_str = tr("Unknown") ; break ;
|
||||||
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
|
case RsTorStatus::OFFLINE: torstatus_str = tr("Tor offline") ; break ;
|
||||||
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
|
case RsTorStatus::READY: torstatus_str = tr("Tor ready") ; break ;
|
||||||
}
|
}
|
||||||
|
|
||||||
torStatus_LB->setText(torstatus_str) ;
|
torStatus_LB->setText(torstatus_str) ;
|
||||||
|
|
||||||
if(torstatus == Tor::TorControl::TorUnknown)
|
if(torstatus == RsTorStatus::UNKNOWN)
|
||||||
torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ;
|
torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ;
|
||||||
else
|
else
|
||||||
torStatus_LB->setToolTip("") ;
|
torStatus_LB->setToolTip("") ;
|
||||||
|
|
||||||
QVariantMap qvm = mTorManager->control()->bootstrapStatus();
|
std::map<std::string,std::string> qvm = RsTor::bootstrapStatus();
|
||||||
QString bootstrapstatus_str ;
|
QString bootstrapstatus_str ;
|
||||||
|
|
||||||
std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl;
|
std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl;
|
||||||
std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl;
|
std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl;
|
||||||
|
|
||||||
std::cerr << "Bootstrap status map: " << std::endl;
|
std::cerr << "Bootstrap status map: " << std::endl;
|
||||||
|
|
||||||
for(auto it(qvm.begin());it!=qvm.end();++it)
|
for(auto it(qvm.begin());it!=qvm.end();++it)
|
||||||
std::cerr << " " << it.key().toStdString() << " : " << it.value().toString().toStdString() << std::endl;
|
std::cerr << " " << it->first << " : " << it->second << std::endl;
|
||||||
|
|
||||||
if(!qvm["progress"].toString().isNull())
|
if(!qvm["progress"].empty())
|
||||||
torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ;
|
torBootstrapStatus_LB->setText(QString::fromStdString(qvm["progress"]) + " % (" + QString::fromStdString(qvm["summary"]) + ")") ;
|
||||||
else
|
else
|
||||||
torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ;
|
torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ;
|
||||||
|
|
||||||
QString service_id ;
|
std::string service_id ;
|
||||||
QString onion_address ;
|
std::string onion_address ;
|
||||||
QHostAddress service_target_address ;
|
std::string service_target_address ;
|
||||||
uint16_t service_port ;
|
uint16_t service_port ;
|
||||||
uint16_t target_port ;
|
uint16_t target_port ;
|
||||||
|
|
||||||
if(mTorManager->getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
|
if(RsTor::getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
|
||||||
{
|
{
|
||||||
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port));
|
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + QString::fromStdString(service_target_address) + ":" + QString::number(target_port));
|
||||||
onionAddress_LB->setText(onion_address);
|
onionAddress_LB->setText(QString::fromStdString(onion_address));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -123,34 +139,39 @@ void TorControlDialog::statusChanged()
|
|||||||
showLog();
|
showLog();
|
||||||
adjustSize();
|
adjustSize();
|
||||||
|
|
||||||
QCoreApplication::processEvents(); // forces update
|
QCoreApplication::processEvents(); // forces update
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorControlDialog::showLog()
|
void TorControlDialog::showLog()
|
||||||
{
|
{
|
||||||
static std::set<QString> already_seen ;
|
static std::set<std::string> already_seen ;
|
||||||
|
|
||||||
QString s ;
|
std::string s ;
|
||||||
QStringList logmsgs = mTorManager->logMessages() ;
|
std::list<std::string> logmsgs = RsTor::logMessages() ;
|
||||||
bool can_print = false ;
|
bool can_print = false ;
|
||||||
|
|
||||||
for(QStringList::const_iterator it(logmsgs.begin());it!=logmsgs.end();++it)
|
for(auto it(logmsgs.begin());it!=logmsgs.end();++it)
|
||||||
{
|
{
|
||||||
s += *it + "\n" ;
|
s += *it + "\n" ;
|
||||||
|
|
||||||
if(already_seen.find(*it) == already_seen.end())
|
if(already_seen.find(*it) == already_seen.end())
|
||||||
{
|
{
|
||||||
can_print = true ;
|
can_print = true ;
|
||||||
already_seen.insert(*it);
|
already_seen.insert(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(can_print)
|
if(can_print)
|
||||||
std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl;
|
{
|
||||||
}
|
std::cerr << "[TOR DEBUG LOG] " << *it << std::endl;
|
||||||
|
|
||||||
// torLog_TB->setText(s) ;:
|
QString s = QString::fromStdString(*it);
|
||||||
|
int n = s.indexOf(QString("Bootstrapped"));
|
||||||
|
|
||||||
std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl;
|
if(n >= 0)
|
||||||
|
torBootstrapStatus_LB->setText(s.mid(n+QString("Bootstrapped").length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//std::cerr << "Connexion Proxy: " << RsTor::socksAddress() << ":" << QString::number(RsTor::socksPort()).toStdString() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
||||||
@ -161,9 +182,9 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
|||||||
return TorControlDialog::TOR_STATUS_FAIL ;
|
return TorControlDialog::TOR_STATUS_FAIL ;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(mTorManager->control()->torStatus())
|
switch(RsTor::torStatus())
|
||||||
{
|
{
|
||||||
case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
|
case RsTorStatus::READY: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
|
||||||
default:
|
default:
|
||||||
return TOR_STATUS_UNKNOWN ;
|
return TOR_STATUS_UNKNOWN ;
|
||||||
}
|
}
|
||||||
@ -171,60 +192,35 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
|||||||
|
|
||||||
TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService()
|
TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService()
|
||||||
{
|
{
|
||||||
std::cerr << "Checking for hidden services:" ;
|
std::cerr << "Checking for hidden services:" ;
|
||||||
|
|
||||||
switch(mHiddenServiceStatus)
|
std::string service_id;
|
||||||
{
|
|
||||||
default:
|
|
||||||
case HIDDEN_SERVICE_STATUS_UNKNOWN: {
|
|
||||||
|
|
||||||
std::cerr << " trying to setup. " ;
|
RsTorHiddenServiceStatus service_status = RsTor::getHiddenServiceStatus(service_id);
|
||||||
|
|
||||||
if(!mTorManager->setupHiddenService())
|
if(service_id.empty())
|
||||||
{
|
{
|
||||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_FAIL ;
|
std::cerr << "Not ready yet." << std::endl;
|
||||||
std::cerr << "Failed." << std::endl;
|
return HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||||
return mHiddenServiceStatus ;
|
}
|
||||||
}
|
else
|
||||||
std::cerr << "Done." << std::endl;
|
{
|
||||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_REQUESTED ;
|
if(mHiddenService.empty())
|
||||||
return mHiddenServiceStatus ;
|
mHiddenService = service_id ;
|
||||||
}
|
|
||||||
|
|
||||||
case HIDDEN_SERVICE_STATUS_REQUESTED: {
|
std::cerr << "New service acquired. Status is " << (int)service_status ;
|
||||||
QList<Tor::HiddenService*> hidden_services = mTorManager->control()->hiddenServices();
|
|
||||||
|
|
||||||
if(hidden_services.empty())
|
if(service_status == RsTorHiddenServiceStatus::ONLINE)
|
||||||
{
|
{
|
||||||
std::cerr << "Not ready yet." << std::endl;
|
std::cerr << ": published and running!" << std::endl;
|
||||||
return mHiddenServiceStatus ;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(mHiddenService == NULL)
|
|
||||||
mHiddenService = *(hidden_services.begin()) ;
|
|
||||||
|
|
||||||
Tor::HiddenService::Status hss = mHiddenService->status();
|
return HIDDEN_SERVICE_STATUS_OK ;
|
||||||
|
}
|
||||||
std::cerr << "New service acquired. Status is " << hss ;
|
else
|
||||||
|
{
|
||||||
if(hss == Tor::HiddenService::Online)
|
std::cerr << ": not ready yet." << std::endl;
|
||||||
{
|
return HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||||
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_OK ;
|
}
|
||||||
std::cerr << ": published and running!" << std::endl;
|
}
|
||||||
|
|
||||||
return mHiddenServiceStatus ;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cerr << ": not ready yet." << std::endl;
|
|
||||||
return mHiddenServiceStatus ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case HIDDEN_SERVICE_STATUS_OK :
|
|
||||||
std::cerr << "New service acquired." << std::endl;
|
|
||||||
return mHiddenServiceStatus ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#include "retroshare/rsevents.h"
|
||||||
|
#include "retroshare/rstor.h"
|
||||||
#include "ui_TorControlWindow.h"
|
#include "ui_TorControlWindow.h"
|
||||||
|
|
||||||
class QTcpServer ;
|
class QTcpServer ;
|
||||||
@ -12,7 +14,8 @@ class TorControlDialog: public QWidget, public Ui::TorControlDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL);
|
TorControlDialog(QWidget *parent =NULL);
|
||||||
|
virtual ~TorControlDialog();
|
||||||
|
|
||||||
enum TorStatus {
|
enum TorStatus {
|
||||||
TOR_STATUS_UNKNOWN = 0x00,
|
TOR_STATUS_UNKNOWN = 0x00,
|
||||||
@ -34,15 +37,14 @@ public:
|
|||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void showLog();
|
void showLog();
|
||||||
void statusChanged();
|
void statusChanged(RsTorStatus torstatus,RsTorConnectivityStatus tor_control_status);
|
||||||
void onIncomingConnection();
|
void onIncomingConnection();
|
||||||
|
|
||||||
|
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||||
private:
|
private:
|
||||||
QString mErrorMsg ;
|
QString mErrorMsg ;
|
||||||
HiddenServiceStatus mHiddenServiceStatus ;
|
std::string mHiddenService;
|
||||||
|
|
||||||
Tor::TorManager *mTorManager ;
|
|
||||||
Tor::HiddenService *mHiddenService ;
|
|
||||||
|
|
||||||
QTcpServer *mIncomingServer ;
|
QTcpServer *mIncomingServer ;
|
||||||
|
RsEventsHandlerId_t mEventHandlerId;
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,12 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
@ -55,6 +61,12 @@
|
|||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="torStatusTxt_LB">
|
<widget class="QLabel" name="torStatusTxt_LB">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Tor status:</string>
|
<string>Tor status:</string>
|
||||||
</property>
|
</property>
|
||||||
@ -79,8 +91,14 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="HiddenServiceAddressTxt_LB">
|
<widget class="QLabel" name="HiddenServiceAddressTxt_LB">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hidden service address:</string>
|
<string>Hidden address:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
@ -89,8 +107,14 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="torBootstrapStatusTxt_LB">
|
<widget class="QLabel" name="torBootstrapStatusTxt_LB">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Tor bootstrap status:</string>
|
<string>Tor status:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
@ -106,6 +130,12 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="onionAddressTxt_LB">
|
<widget class="QLabel" name="onionAddressTxt_LB">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Onion address:</string>
|
<string>Onion address:</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1,526 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "TorManager.h"
|
|
||||||
#include "TorProcess.h"
|
|
||||||
#include "TorControl.h"
|
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "HiddenService.h"
|
|
||||||
#include "GetConfCommand.h"
|
|
||||||
#include "Settings.h"
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QTcpServer>
|
|
||||||
#include <QTextStream>
|
|
||||||
|
|
||||||
using namespace Tor;
|
|
||||||
|
|
||||||
namespace Tor
|
|
||||||
{
|
|
||||||
|
|
||||||
class TorManagerPrivate : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
TorManager *q;
|
|
||||||
TorProcess *process;
|
|
||||||
TorControl *control;
|
|
||||||
QString dataDir;
|
|
||||||
QString hiddenServiceDir;
|
|
||||||
QStringList logMessages;
|
|
||||||
QString errorMessage;
|
|
||||||
bool configNeeded;
|
|
||||||
|
|
||||||
HiddenService *hiddenService ;
|
|
||||||
|
|
||||||
explicit TorManagerPrivate(TorManager *parent = 0);
|
|
||||||
|
|
||||||
QString torExecutablePath() const;
|
|
||||||
bool createDataDir(const QString &path);
|
|
||||||
bool createDefaultTorrc(const QString &path);
|
|
||||||
|
|
||||||
void setError(const QString &errorMessage);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void processStateChanged(int state);
|
|
||||||
void processErrorChanged(const QString &errorMessage);
|
|
||||||
void processLogMessage(const QString &message);
|
|
||||||
void controlStatusChanged(int status);
|
|
||||||
void getConfFinished();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TorManager::TorManager(QObject *parent)
|
|
||||||
: QObject(parent), d(new TorManagerPrivate(this))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TorManagerPrivate::TorManagerPrivate(TorManager *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, q(parent)
|
|
||||||
, process(0)
|
|
||||||
, control(new TorControl(this))
|
|
||||||
, configNeeded(false)
|
|
||||||
, hiddenService(NULL)
|
|
||||||
{
|
|
||||||
connect(control, SIGNAL(statusChanged(int,int)), SLOT(controlStatusChanged(int)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TorManager *TorManager::instance()
|
|
||||||
{
|
|
||||||
static TorManager *p = 0;
|
|
||||||
if (!p)
|
|
||||||
p = new TorManager(qApp);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorControl *TorManager::control()
|
|
||||||
{
|
|
||||||
return d->control;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorProcess *TorManager::process()
|
|
||||||
{
|
|
||||||
return d->process;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::isTorAvailable()
|
|
||||||
{
|
|
||||||
return !instance()->d->torExecutablePath().isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorManager::torDataDirectory() const
|
|
||||||
{
|
|
||||||
return d->dataDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManager::setTorDataDirectory(const QString &path)
|
|
||||||
{
|
|
||||||
d->dataDir = QDir::fromNativeSeparators(path);
|
|
||||||
|
|
||||||
if (!d->dataDir.isEmpty() && !d->dataDir.endsWith(QLatin1Char('/')))
|
|
||||||
d->dataDir.append(QLatin1Char('/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorManager::hiddenServiceDirectory() const
|
|
||||||
{
|
|
||||||
return d->hiddenServiceDir;
|
|
||||||
}
|
|
||||||
void TorManager::setHiddenServiceDirectory(const QString &path)
|
|
||||||
{
|
|
||||||
d->hiddenServiceDir = QDir::fromNativeSeparators(path);
|
|
||||||
|
|
||||||
if (!d->hiddenServiceDir.isEmpty() && !d->hiddenServiceDir.endsWith(QLatin1Char('/')))
|
|
||||||
d->hiddenServiceDir.append(QLatin1Char('/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::setupHiddenService()
|
|
||||||
{
|
|
||||||
if(d->hiddenService != NULL)
|
|
||||||
{
|
|
||||||
std::cerr << "TorManager: setupHiddenService() called twice! Not doing anything this time." << std::endl;
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString keyData ;//= m_settings->read("serviceKey").toString();
|
|
||||||
QString legacyDir = d->hiddenServiceDir;
|
|
||||||
|
|
||||||
std::cerr << "TorManager: setting up hidden service." << std::endl;
|
|
||||||
|
|
||||||
if(legacyDir.isNull())
|
|
||||||
{
|
|
||||||
std::cerr << "legacy dir not set! Cannot proceed." << std::endl;
|
|
||||||
return false ;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << "Using legacy dir: " << legacyDir.toStdString() << std::endl;
|
|
||||||
|
|
||||||
if (!legacyDir.isEmpty() && QFile::exists(legacyDir + QLatin1String("/private_key")))
|
|
||||||
{
|
|
||||||
std::cerr << "Attempting to load key from legacy filesystem format in " << legacyDir.toStdString() << std::endl;
|
|
||||||
|
|
||||||
CryptoKey key;
|
|
||||||
if (!key.loadFromFile(legacyDir + QLatin1String("/private_key")))
|
|
||||||
{
|
|
||||||
qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->hiddenService = new Tor::HiddenService(key, legacyDir, this);
|
|
||||||
|
|
||||||
std::cerr << "Got key from legacy dir: " << std::endl;
|
|
||||||
std::cerr << key.bytes().toStdString() << std::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->hiddenService = new Tor::HiddenService(legacyDir, this);
|
|
||||||
|
|
||||||
std::cerr << "Creating new hidden service." << std::endl;
|
|
||||||
|
|
||||||
connect(d->hiddenService, SIGNAL(privateKeyChanged()), this, SLOT(hiddenServicePrivateKeyChanged())) ;
|
|
||||||
connect(d->hiddenService, SIGNAL(hostnameChanged()), this, SLOT(hiddenServiceHostnameChanged())) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(d->hiddenService);
|
|
||||||
connect(d->hiddenService, SIGNAL(statusChanged(int,int)), this, SLOT(hiddenServiceStatusChanged(int,int)));
|
|
||||||
|
|
||||||
// Generally, these are not used, and we bind to localhost and port 0
|
|
||||||
// for an automatic (and portable) selection.
|
|
||||||
|
|
||||||
QHostAddress address = QHostAddress::LocalHost; // we only listen from localhost
|
|
||||||
|
|
||||||
quint16 port = 7934;//(quint16)m_settings->read("localListenPort").toInt();
|
|
||||||
|
|
||||||
std::cerr << "Testing host address: " << address.toString().toStdString() << ":" << port ;
|
|
||||||
|
|
||||||
if (!QTcpServer().listen(address, port))
|
|
||||||
{
|
|
||||||
// XXX error case
|
|
||||||
std::cerr << " Failed to open incoming socket" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << " OK - Adding hidden service to TorControl." << std::endl;
|
|
||||||
|
|
||||||
//d->hiddenService->addTarget(9878, mIncomingServer->serverAddress(), mIncomingServer->serverPort());
|
|
||||||
d->hiddenService->addTarget(9878, QHostAddress::LocalHost,7934);
|
|
||||||
control()->addHiddenService(d->hiddenService);
|
|
||||||
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManager::hiddenServiceStatusChanged(int old_status,int new_status)
|
|
||||||
{
|
|
||||||
std::cerr << "Hidden service status changed from " << old_status << " to " << new_status << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManager::hiddenServicePrivateKeyChanged()
|
|
||||||
{
|
|
||||||
QString key = QString::fromLatin1(d->hiddenService->privateKey().bytes());
|
|
||||||
|
|
||||||
QFile outfile(d->hiddenServiceDir + QLatin1String("/private_key")) ;
|
|
||||||
outfile.open( QIODevice::WriteOnly | QIODevice::Text );
|
|
||||||
QTextStream s(&outfile);
|
|
||||||
|
|
||||||
#ifdef TO_REMOVE
|
|
||||||
s << "-----BEGIN RSA PRIVATE KEY-----" << endl;
|
|
||||||
|
|
||||||
for(int i=0;i<key.length();i+=64)
|
|
||||||
s << key.mid(i,64) << endl ;
|
|
||||||
|
|
||||||
s << "-----END RSA PRIVATE KEY-----" << endl;
|
|
||||||
#endif
|
|
||||||
s << key ;
|
|
||||||
|
|
||||||
outfile.close();
|
|
||||||
|
|
||||||
std::cerr << "Hidden service private key changed!" << std::endl;
|
|
||||||
std::cerr << key.toStdString() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManager::hiddenServiceHostnameChanged()
|
|
||||||
{
|
|
||||||
QFile outfile2(d->hiddenServiceDir + QLatin1String("/hostname")) ;
|
|
||||||
outfile2.open( QIODevice::WriteOnly | QIODevice::Text );
|
|
||||||
QTextStream t(&outfile2);
|
|
||||||
|
|
||||||
QString hostname(d->hiddenService->hostname());
|
|
||||||
|
|
||||||
t << hostname << endl;
|
|
||||||
outfile2.close();
|
|
||||||
|
|
||||||
std::cerr << "Hidden service hostname changed: " << hostname.toStdString() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::configurationNeeded() const
|
|
||||||
{
|
|
||||||
return d->configNeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList TorManager::logMessages() const
|
|
||||||
{
|
|
||||||
return d->logMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::hasError() const
|
|
||||||
{
|
|
||||||
return !d->errorMessage.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorManager::errorMessage() const
|
|
||||||
{
|
|
||||||
return d->errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::start()
|
|
||||||
{
|
|
||||||
if (!d->errorMessage.isEmpty()) {
|
|
||||||
d->errorMessage.clear();
|
|
||||||
emit errorChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsObject settings(QStringLiteral("tor"));
|
|
||||||
|
|
||||||
// If a control port is defined by config or environment, skip launching tor
|
|
||||||
if (!settings.read("controlPort").isUndefined() ||
|
|
||||||
!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT"))
|
|
||||||
{
|
|
||||||
QHostAddress address(settings.read("controlAddress").toString());
|
|
||||||
quint16 port = (quint16)settings.read("controlPort").toInt();
|
|
||||||
QByteArray password = settings.read("controlPassword").toString().toLatin1();
|
|
||||||
|
|
||||||
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_HOST"))
|
|
||||||
address = QHostAddress(QString::fromLatin1(qgetenv("TOR_CONTROL_HOST")));
|
|
||||||
|
|
||||||
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) {
|
|
||||||
bool ok = false;
|
|
||||||
port = qgetenv("TOR_CONTROL_PORT").toUShort(&ok);
|
|
||||||
if (!ok)
|
|
||||||
port = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PASSWD"))
|
|
||||||
password = qgetenv("TOR_CONTROL_PASSWD");
|
|
||||||
|
|
||||||
if (!port) {
|
|
||||||
d->setError(QStringLiteral("Invalid control port settings from environment or configuration"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address.isNull())
|
|
||||||
address = QHostAddress::LocalHost;
|
|
||||||
|
|
||||||
d->control->setAuthPassword(password);
|
|
||||||
d->control->connect(address, port);
|
|
||||||
} else {
|
|
||||||
// Launch a bundled Tor instance
|
|
||||||
QString executable = d->torExecutablePath();
|
|
||||||
|
|
||||||
std::cerr << "Executable path: " << executable.toStdString() << std::endl;
|
|
||||||
|
|
||||||
if (executable.isEmpty()) {
|
|
||||||
d->setError(QStringLiteral("Cannot find tor executable"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!d->process) {
|
|
||||||
d->process = new TorProcess(this);
|
|
||||||
connect(d->process, SIGNAL(stateChanged(int)), d, SLOT(processStateChanged(int)));
|
|
||||||
connect(d->process, SIGNAL(errorMessageChanged(QString)), d,
|
|
||||||
SLOT(processErrorChanged(QString)));
|
|
||||||
connect(d->process, SIGNAL(logMessage(QString)), d, SLOT(processLogMessage(QString)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!QFile::exists(d->dataDir) && !d->createDataDir(d->dataDir)) {
|
|
||||||
d->setError(QStringLiteral("Cannot write data location: %1").arg(d->dataDir));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString defaultTorrc = d->dataDir + QStringLiteral("default_torrc");
|
|
||||||
if (!QFile::exists(defaultTorrc) && !d->createDefaultTorrc(defaultTorrc)) {
|
|
||||||
d->setError(QStringLiteral("Cannot write data files: %1").arg(defaultTorrc));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile torrc(d->dataDir + QStringLiteral("torrc"));
|
|
||||||
if (!torrc.exists() || torrc.size() == 0) {
|
|
||||||
d->configNeeded = true;
|
|
||||||
emit configurationNeededChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
d->process->setExecutable(executable);
|
|
||||||
d->process->setDataDir(d->dataDir);
|
|
||||||
d->process->setDefaultTorrc(defaultTorrc);
|
|
||||||
d->process->start();
|
|
||||||
}
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port)
|
|
||||||
{
|
|
||||||
proxy_server_adress = control()->socksAddress();
|
|
||||||
proxy_server_port = control()->socksPort();
|
|
||||||
|
|
||||||
return proxy_server_port > 1023 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManager::getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port)
|
|
||||||
{
|
|
||||||
QList<Tor::HiddenService*> hidden_services = control()->hiddenServices();
|
|
||||||
|
|
||||||
if(hidden_services.empty())
|
|
||||||
return false ;
|
|
||||||
|
|
||||||
// Only return the first one.
|
|
||||||
|
|
||||||
for(auto it(hidden_services.begin());it!=hidden_services.end();++it)
|
|
||||||
{
|
|
||||||
service_onion_address = (*it)->hostname();
|
|
||||||
service_id = (*it)->serviceId();
|
|
||||||
|
|
||||||
for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2)
|
|
||||||
{
|
|
||||||
service_port = (*it2).servicePort ;
|
|
||||||
service_target_address = (*it2).targetAddress ;
|
|
||||||
target_port = (*it2).targetPort;
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
break ;
|
|
||||||
}
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManagerPrivate::processStateChanged(int state)
|
|
||||||
{
|
|
||||||
std::cerr << Q_FUNC_INFO << "state: " << state << " passwd=\"" << QString(process->controlPassword()).toStdString() << "\" " << process->controlHost().toString().toStdString()
|
|
||||||
<< ":" << process->controlPort() << std::endl;
|
|
||||||
if (state == TorProcess::Ready) {
|
|
||||||
control->setAuthPassword(process->controlPassword());
|
|
||||||
control->connect(process->controlHost(), process->controlPort());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManagerPrivate::processErrorChanged(const QString &errorMessage)
|
|
||||||
{
|
|
||||||
std::cerr << "tor error:" << errorMessage.toStdString() << std::endl;
|
|
||||||
setError(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManagerPrivate::processLogMessage(const QString &message)
|
|
||||||
{
|
|
||||||
std::cerr << "tor:" << message.toStdString() << std::endl;
|
|
||||||
if (logMessages.size() >= 50)
|
|
||||||
logMessages.takeFirst();
|
|
||||||
logMessages.append(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManagerPrivate::controlStatusChanged(int status)
|
|
||||||
{
|
|
||||||
if (status == TorControl::Connected) {
|
|
||||||
if (!configNeeded) {
|
|
||||||
// If DisableNetwork is 1, trigger configurationNeeded
|
|
||||||
connect(control->getConfiguration(QStringLiteral("DisableNetwork")),
|
|
||||||
SIGNAL(finished()), SLOT(getConfFinished()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process) {
|
|
||||||
// Take ownership via this control socket
|
|
||||||
control->takeOwnership();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManagerPrivate::getConfFinished()
|
|
||||||
{
|
|
||||||
GetConfCommand *command = qobject_cast<GetConfCommand*>(sender());
|
|
||||||
if (!command)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (command->get("DisableNetwork").toInt() == 1 && !configNeeded) {
|
|
||||||
configNeeded = true;
|
|
||||||
emit q->configurationNeededChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorManagerPrivate::torExecutablePath() const
|
|
||||||
{
|
|
||||||
SettingsObject settings(QStringLiteral("tor"));
|
|
||||||
QString path = settings.read("executablePath").toString();
|
|
||||||
|
|
||||||
if (!path.isEmpty() && QFile::exists(path))
|
|
||||||
return path;
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
QString filename(QStringLiteral("/tor/tor.exe"));
|
|
||||||
#else
|
|
||||||
QString filename(QStringLiteral("/tor"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
path = qApp->applicationDirPath();
|
|
||||||
|
|
||||||
if (QFile::exists(path + filename))
|
|
||||||
return path + filename;
|
|
||||||
|
|
||||||
#ifdef BUNDLED_TOR_PATH
|
|
||||||
path = QStringLiteral(BUNDLED_TOR_PATH);
|
|
||||||
if (QFile::exists(path + filename))
|
|
||||||
return path + filename;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
// on MacOS, try traditional brew installation path
|
|
||||||
|
|
||||||
path = QStringLiteral("/usr/local/opt/tor/bin") ;
|
|
||||||
|
|
||||||
if (QFile::exists(path + filename))
|
|
||||||
return path + filename;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Try $PATH
|
|
||||||
return filename.mid(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManagerPrivate::createDataDir(const QString &path)
|
|
||||||
{
|
|
||||||
QDir dir(path);
|
|
||||||
return dir.mkpath(QStringLiteral("."));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorManagerPrivate::createDefaultTorrc(const QString &path)
|
|
||||||
{
|
|
||||||
static const char defaultTorrcContent[] =
|
|
||||||
"SocksPort auto\n"
|
|
||||||
"AvoidDiskWrites 1\n"
|
|
||||||
// "DisableNetwork 1\n" // (cyril) I removed this because it prevents Tor to bootstrap.
|
|
||||||
"__ReloadTorrcOnSIGHUP 0\n";
|
|
||||||
|
|
||||||
QFile file(path);
|
|
||||||
if (!file.open(QIODevice::WriteOnly))
|
|
||||||
return false;
|
|
||||||
if (file.write(defaultTorrcContent) < 0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorManagerPrivate::setError(const QString &message)
|
|
||||||
{
|
|
||||||
errorMessage = message;
|
|
||||||
emit q->errorChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "TorManager.moc"
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "TorProcess_p.h"
|
|
||||||
#include "CryptoKey.h"
|
|
||||||
#include "SecureRNG.h"
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
|
|
||||||
using namespace Tor;
|
|
||||||
|
|
||||||
TorProcess::TorProcess(QObject *parent)
|
|
||||||
: QObject(parent), d(new TorProcessPrivate(this))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TorProcess::~TorProcess()
|
|
||||||
{
|
|
||||||
if (state() > NotStarted)
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
TorProcessPrivate::TorProcessPrivate(TorProcess *q)
|
|
||||||
: QObject(q), q(q), state(TorProcess::NotStarted), controlPort(0), controlPortAttempts(0)
|
|
||||||
{
|
|
||||||
connect(&process, &QProcess::started, this, &TorProcessPrivate::processStarted);
|
|
||||||
connect(&process, (void (QProcess::*)(int, QProcess::ExitStatus))&QProcess::finished,
|
|
||||||
this, &TorProcessPrivate::processFinished);
|
|
||||||
connect(&process, (void (QProcess::*)(QProcess::ProcessError))&QProcess::error,
|
|
||||||
this, &TorProcessPrivate::processError);
|
|
||||||
connect(&process, &QProcess::readyRead, this, &TorProcessPrivate::processReadable);
|
|
||||||
|
|
||||||
controlPortTimer.setInterval(500);
|
|
||||||
connect(&controlPortTimer, &QTimer::timeout, this, &TorProcessPrivate::tryReadControlPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorProcess::executable() const
|
|
||||||
{
|
|
||||||
return d->executable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcess::setExecutable(const QString &path)
|
|
||||||
{
|
|
||||||
d->executable = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorProcess::dataDir() const
|
|
||||||
{
|
|
||||||
return d->dataDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcess::setDataDir(const QString &path)
|
|
||||||
{
|
|
||||||
d->dataDir = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorProcess::defaultTorrc() const
|
|
||||||
{
|
|
||||||
return d->defaultTorrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcess::setDefaultTorrc(const QString &path)
|
|
||||||
{
|
|
||||||
d->defaultTorrc = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList TorProcess::extraSettings() const
|
|
||||||
{
|
|
||||||
return d->extraSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcess::setExtraSettings(const QStringList &settings)
|
|
||||||
{
|
|
||||||
d->extraSettings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorProcess::State TorProcess::state() const
|
|
||||||
{
|
|
||||||
return d->state;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorProcess::errorMessage() const
|
|
||||||
{
|
|
||||||
return d->errorMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcess::start()
|
|
||||||
{
|
|
||||||
if (state() > NotStarted)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->errorMessage.clear();
|
|
||||||
|
|
||||||
if (d->executable.isEmpty() || d->dataDir.isEmpty()) {
|
|
||||||
d->errorMessage = QStringLiteral("Tor executable and data directory not specified");
|
|
||||||
d->state = Failed;
|
|
||||||
emit errorMessageChanged(d->errorMessage);
|
|
||||||
emit stateChanged(d->state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!d->ensureFilesExist()) {
|
|
||||||
d->state = Failed;
|
|
||||||
emit errorMessageChanged(d->errorMessage);
|
|
||||||
emit stateChanged(d->state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray password = controlPassword();
|
|
||||||
QByteArray hashedPassword = torControlHashedPassword(password);
|
|
||||||
if (password.isEmpty() || hashedPassword.isEmpty()) {
|
|
||||||
d->errorMessage = QStringLiteral("Random password generation failed");
|
|
||||||
d->state = Failed;
|
|
||||||
emit errorMessageChanged(d->errorMessage);
|
|
||||||
emit stateChanged(d->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList args;
|
|
||||||
if (!d->defaultTorrc.isEmpty())
|
|
||||||
args << QStringLiteral("--defaults-torrc") << d->defaultTorrc;
|
|
||||||
args << QStringLiteral("-f") << d->torrcPath();
|
|
||||||
args << QStringLiteral("DataDirectory") << d->dataDir;
|
|
||||||
args << QStringLiteral("HashedControlPassword") << QString::fromLatin1(hashedPassword);
|
|
||||||
args << QStringLiteral("ControlPort") << QStringLiteral("auto");
|
|
||||||
args << QStringLiteral("ControlPortWriteToFile") << d->controlPortFilePath();
|
|
||||||
args << QStringLiteral("__OwningControllerProcess") << QString::number(qApp->applicationPid());
|
|
||||||
args << d->extraSettings;
|
|
||||||
|
|
||||||
d->state = Starting;
|
|
||||||
emit stateChanged(d->state);
|
|
||||||
|
|
||||||
if (QFile::exists(d->controlPortFilePath()))
|
|
||||||
QFile::remove(d->controlPortFilePath());
|
|
||||||
d->controlPort = 0;
|
|
||||||
d->controlHost.clear();
|
|
||||||
|
|
||||||
d->process.setProcessChannelMode(QProcess::MergedChannels);
|
|
||||||
d->process.start(d->executable, args, QIODevice::ReadOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcess::stop()
|
|
||||||
{
|
|
||||||
if (state() < Starting)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->controlPortTimer.stop();
|
|
||||||
|
|
||||||
if (d->process.state() == QProcess::Starting)
|
|
||||||
d->process.waitForStarted(2000);
|
|
||||||
|
|
||||||
d->state = NotStarted;
|
|
||||||
|
|
||||||
// Windows can't terminate the process well, but Tor will clean itself up
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
if (d->process.state() == QProcess::Running) {
|
|
||||||
d->process.terminate();
|
|
||||||
if (!d->process.waitForFinished(5000)) {
|
|
||||||
qWarning() << "Tor process" << d->process.pid() << "did not respond to terminate, killing...";
|
|
||||||
d->process.kill();
|
|
||||||
if (!d->process.waitForFinished(2000)) {
|
|
||||||
qCritical() << "Tor process" << d->process.pid() << "did not respond to kill!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
emit stateChanged(d->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray TorProcess::controlPassword()
|
|
||||||
{
|
|
||||||
if (d->controlPassword.isEmpty())
|
|
||||||
d->controlPassword = SecureRNG::randomPrintable(16);
|
|
||||||
return d->controlPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
QHostAddress TorProcess::controlHost()
|
|
||||||
{
|
|
||||||
return d->controlHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint16 TorProcess::controlPort()
|
|
||||||
{
|
|
||||||
return d->controlPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorProcessPrivate::ensureFilesExist()
|
|
||||||
{
|
|
||||||
QFile torrc(torrcPath());
|
|
||||||
if (!torrc.exists()) {
|
|
||||||
QDir dir(dataDir);
|
|
||||||
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
|
|
||||||
errorMessage = QStringLiteral("Cannot create Tor data directory: %1").arg(dataDir);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!torrc.open(QIODevice::ReadWrite)) {
|
|
||||||
errorMessage = QStringLiteral("Cannot create Tor configuration file: %1").arg(torrcPath());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorProcessPrivate::torrcPath() const
|
|
||||||
{
|
|
||||||
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("torrc");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorProcessPrivate::controlPortFilePath() const
|
|
||||||
{
|
|
||||||
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("control-port");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcessPrivate::processStarted()
|
|
||||||
{
|
|
||||||
state = TorProcess::Connecting;
|
|
||||||
emit q->stateChanged(state);
|
|
||||||
|
|
||||||
controlPortAttempts = 0;
|
|
||||||
controlPortTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcessPrivate::processFinished()
|
|
||||||
{
|
|
||||||
if (state < TorProcess::Starting)
|
|
||||||
return;
|
|
||||||
|
|
||||||
controlPortTimer.stop();
|
|
||||||
errorMessage = process.errorString();
|
|
||||||
if (errorMessage.isEmpty())
|
|
||||||
errorMessage = QStringLiteral("Process exited unexpectedly (code %1)").arg(process.exitCode());
|
|
||||||
state = TorProcess::Failed;
|
|
||||||
emit q->errorMessageChanged(errorMessage);
|
|
||||||
emit q->stateChanged(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcessPrivate::processError(QProcess::ProcessError error)
|
|
||||||
{
|
|
||||||
if (error == QProcess::FailedToStart || error == QProcess::Crashed)
|
|
||||||
processFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcessPrivate::processReadable()
|
|
||||||
{
|
|
||||||
while (process.bytesAvailable() > 0) {
|
|
||||||
QByteArray line = process.readLine(2048).trimmed();
|
|
||||||
if (!line.isEmpty())
|
|
||||||
emit q->logMessage(QString::fromLatin1(line));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorProcessPrivate::tryReadControlPort()
|
|
||||||
{
|
|
||||||
QFile file(controlPortFilePath());
|
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
QByteArray data = file.readLine().trimmed();
|
|
||||||
|
|
||||||
int p;
|
|
||||||
if (data.startsWith("PORT=") && (p = data.lastIndexOf(':')) > 0) {
|
|
||||||
controlHost = QHostAddress(QString::fromLatin1(data.mid(5, p - 5)));
|
|
||||||
controlPort = data.mid(p+1).toUShort();
|
|
||||||
|
|
||||||
if (!controlHost.isNull() && controlPort > 0) {
|
|
||||||
controlPortTimer.stop();
|
|
||||||
state = TorProcess::Ready;
|
|
||||||
emit q->stateChanged(state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++controlPortAttempts * controlPortTimer.interval() > 10000) {
|
|
||||||
errorMessage = QStringLiteral("No control port available after launching process");
|
|
||||||
state = TorProcess::Failed;
|
|
||||||
emit q->errorMessageChanged(errorMessage);
|
|
||||||
emit q->stateChanged(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TORPROCESS_P_H
|
|
||||||
#define TORPROCESS_P_H
|
|
||||||
|
|
||||||
#include "TorProcess.h"
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
namespace Tor {
|
|
||||||
|
|
||||||
class TorProcessPrivate : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
TorProcess *q;
|
|
||||||
QProcess process;
|
|
||||||
QString executable;
|
|
||||||
QString dataDir;
|
|
||||||
QString defaultTorrc;
|
|
||||||
QStringList extraSettings;
|
|
||||||
TorProcess::State state;
|
|
||||||
QString errorMessage;
|
|
||||||
QHostAddress controlHost;
|
|
||||||
quint16 controlPort;
|
|
||||||
QByteArray controlPassword;
|
|
||||||
|
|
||||||
QTimer controlPortTimer;
|
|
||||||
int controlPortAttempts;
|
|
||||||
|
|
||||||
TorProcessPrivate(TorProcess *q);
|
|
||||||
|
|
||||||
QString torrcPath() const;
|
|
||||||
QString controlPortFilePath() const;
|
|
||||||
bool ensureFilesExist();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void processStarted();
|
|
||||||
void processFinished();
|
|
||||||
void processError(QProcess::ProcessError error);
|
|
||||||
void processReadable();
|
|
||||||
void tryReadControlPort();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "TorSocket.h"
|
|
||||||
#include "TorControl.h"
|
|
||||||
#include <QNetworkProxy>
|
|
||||||
|
|
||||||
using namespace Tor;
|
|
||||||
|
|
||||||
TorSocket::TorSocket(QObject *parent)
|
|
||||||
: QTcpSocket(parent)
|
|
||||||
, m_port(0)
|
|
||||||
, m_reconnectEnabled(true)
|
|
||||||
, m_maxInterval(900)
|
|
||||||
, m_connectAttempts(0)
|
|
||||||
{
|
|
||||||
connect(torControl, SIGNAL(connectivityChanged()), SLOT(connectivityChanged()));
|
|
||||||
connect(&m_connectTimer, SIGNAL(timeout()), SLOT(reconnect()));
|
|
||||||
connect(this, SIGNAL(disconnected()), SLOT(onFailed()));
|
|
||||||
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onFailed()));
|
|
||||||
|
|
||||||
m_connectTimer.setSingleShot(true);
|
|
||||||
connectivityChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
TorSocket::~TorSocket()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::setReconnectEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
if (enabled == m_reconnectEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_reconnectEnabled = enabled;
|
|
||||||
if (m_reconnectEnabled) {
|
|
||||||
m_connectAttempts = 0;
|
|
||||||
reconnect();
|
|
||||||
} else {
|
|
||||||
m_connectTimer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::setMaxAttemptInterval(int interval)
|
|
||||||
{
|
|
||||||
m_maxInterval = interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::resetAttempts()
|
|
||||||
{
|
|
||||||
m_connectAttempts = 0;
|
|
||||||
if (m_connectTimer.isActive()) {
|
|
||||||
m_connectTimer.stop();
|
|
||||||
m_connectTimer.start(reconnectInterval() * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int TorSocket::reconnectInterval()
|
|
||||||
{
|
|
||||||
int delay = 0;
|
|
||||||
if (m_connectAttempts <= 4)
|
|
||||||
delay = 30;
|
|
||||||
else if (m_connectAttempts <= 6)
|
|
||||||
delay = 120;
|
|
||||||
else
|
|
||||||
delay = m_maxInterval;
|
|
||||||
|
|
||||||
return qMin(delay, m_maxInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::reconnect()
|
|
||||||
{
|
|
||||||
if (!torControl->hasConnectivity() || !reconnectEnabled())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_connectTimer.stop();
|
|
||||||
if (!m_host.isEmpty() && m_port) {
|
|
||||||
std::cerr << "Attempting reconnection of socket to" << m_host.toStdString() << ":" << m_port << std::endl;
|
|
||||||
connectToHost(m_host, m_port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::connectivityChanged()
|
|
||||||
{
|
|
||||||
if (torControl->hasConnectivity()) {
|
|
||||||
setProxy(torControl->connectionProxy());
|
|
||||||
if (state() == QAbstractSocket::UnconnectedState)
|
|
||||||
reconnect();
|
|
||||||
} else {
|
|
||||||
m_connectTimer.stop();
|
|
||||||
m_connectAttempts = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode,
|
|
||||||
NetworkLayerProtocol protocol)
|
|
||||||
{
|
|
||||||
m_host = hostName;
|
|
||||||
m_port = port;
|
|
||||||
|
|
||||||
if (!torControl->hasConnectivity())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (proxy() != torControl->connectionProxy())
|
|
||||||
setProxy(torControl->connectionProxy());
|
|
||||||
|
|
||||||
QAbstractSocket::connectToHost(hostName, port, openMode, protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode)
|
|
||||||
{
|
|
||||||
TorSocket::connectToHost(address.toString(), port, openMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorSocket::onFailed()
|
|
||||||
{
|
|
||||||
// Make sure the internal connection to the SOCKS proxy is closed
|
|
||||||
// Otherwise reconnect attempts will fail (#295)
|
|
||||||
close();
|
|
||||||
|
|
||||||
if (reconnectEnabled() && !m_connectTimer.isActive()) {
|
|
||||||
m_connectAttempts++;
|
|
||||||
m_connectTimer.start(reconnectInterval() * 1000);
|
|
||||||
std::cerr << "Reconnecting socket to" << m_host.toStdString() << ":" << m_port << "in" << m_connectTimer.interval() / 1000 << "seconds" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
/* Ricochet - https://ricochet.im/
|
|
||||||
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
*
|
|
||||||
* * Neither the names of the copyright owners nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TORSOCKET_H
|
|
||||||
#define TORSOCKET_H
|
|
||||||
|
|
||||||
#include <QTcpSocket>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
namespace Tor {
|
|
||||||
|
|
||||||
/* Specialized QTcpSocket which makes connections over the SOCKS proxy
|
|
||||||
* from a TorControl instance, automatically attempts reconnections, and
|
|
||||||
* reacts to Tor's connectivity state.
|
|
||||||
*
|
|
||||||
* Use normal QTcpSocket/QAbstractSocket API. When a connection fails, it
|
|
||||||
* will be retried automatically after the correct interval and when
|
|
||||||
* connectivity is available.
|
|
||||||
*
|
|
||||||
* To fully disconnect, destroy the object, or call
|
|
||||||
* setReconnectEnabled(false) and disconnect the socket with
|
|
||||||
* disconnectFromHost or abort.
|
|
||||||
*
|
|
||||||
* The caller is responsible for resetting the attempt counter if a
|
|
||||||
* connection was successful and reconnection will be used again.
|
|
||||||
*/
|
|
||||||
class TorSocket : public QTcpSocket
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TorSocket(QObject *parent = 0);
|
|
||||||
virtual ~TorSocket();
|
|
||||||
|
|
||||||
bool reconnectEnabled() const { return m_reconnectEnabled; }
|
|
||||||
void setReconnectEnabled(bool enabled);
|
|
||||||
int maxAttemptInterval() { return m_maxInterval; }
|
|
||||||
void setMaxAttemptInterval(int interval);
|
|
||||||
void resetAttempts();
|
|
||||||
|
|
||||||
virtual void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
|
|
||||||
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);
|
|
||||||
|
|
||||||
QString hostName() const { return m_host; }
|
|
||||||
quint16 port() const { return m_port; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual int reconnectInterval();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void reconnect();
|
|
||||||
void connectivityChanged();
|
|
||||||
void onFailed();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_host;
|
|
||||||
quint16 m_port;
|
|
||||||
QTimer m_connectTimer;
|
|
||||||
bool m_reconnectEnabled;
|
|
||||||
int m_maxInterval;
|
|
||||||
int m_connectAttempts;
|
|
||||||
|
|
||||||
using QAbstractSocket::connectToHost;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -33,15 +33,15 @@
|
|||||||
|
|
||||||
#include <rshare.h>
|
#include <rshare.h>
|
||||||
#include "gui/settings/rsharesettings.h"
|
#include "gui/settings/rsharesettings.h"
|
||||||
#include "TorControl/TorManager.h"
|
|
||||||
#include "util/misc.h"
|
#include "util/misc.h"
|
||||||
#include "gui/common/FilesDefs.h"
|
#include "gui/common/FilesDefs.h"
|
||||||
|
|
||||||
#include <retroshare/rsidentity.h>
|
#include "retroshare/rstor.h"
|
||||||
#include <retroshare/rsinit.h>
|
#include "retroshare/rsidentity.h"
|
||||||
#include <retroshare/rsnotify.h>
|
#include "retroshare/rsinit.h"
|
||||||
#include <rsserver/rsaccounts.h>
|
#include "retroshare/rsnotify.h"
|
||||||
#include <util/rsrandom.h>
|
#include "rsserver/rsaccounts.h"
|
||||||
|
#include "util/rsrandom.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@ -520,7 +520,7 @@ void GenCertDialog::genPerson()
|
|||||||
bool isHiddenLoc = (ui.nodeType_CB->currentIndex()>0);
|
bool isHiddenLoc = (ui.nodeType_CB->currentIndex()>0);
|
||||||
bool isAutoTor = (ui.nodeType_CB->currentIndex()==1);
|
bool isAutoTor = (ui.nodeType_CB->currentIndex()==1);
|
||||||
|
|
||||||
if(isAutoTor && !Tor::TorManager::isTorAvailable())
|
if(isAutoTor && !RsTor::isTorAvailable())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this,tr("Tor is not available"),tr("No Tor executable has been found on your system. You need to install Tor before creating a hidden identity.")) ;
|
QMessageBox::critical(this,tr("Tor is not available"),tr("No Tor executable has been found on your system. You need to install Tor before creating a hidden identity.")) ;
|
||||||
return ;
|
return ;
|
||||||
|
230
retroshare-gui/src/gui/TorControl/TorControlWindow.cpp
Normal file
230
retroshare-gui/src/gui/TorControl/TorControlWindow.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QGraphicsDropShadowEffect>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "util/rstime.h"
|
||||||
|
|
||||||
|
#include "TorControlWindow.h"
|
||||||
|
#include "TorManager.h"
|
||||||
|
#include "TorControl.h"
|
||||||
|
#include "HiddenService.h"
|
||||||
|
|
||||||
|
TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent)
|
||||||
|
: mTorManager(tm)
|
||||||
|
{
|
||||||
|
setupUi(this) ;
|
||||||
|
|
||||||
|
QObject::connect(tm->control(),SIGNAL(statusChanged(int,int)),this,SLOT(statusChanged())) ;
|
||||||
|
QObject::connect(tm->control(),SIGNAL(connected()),this,SLOT(statusChanged()));
|
||||||
|
QObject::connect(tm->control(),SIGNAL(disconnected()),this,SLOT(statusChanged()));
|
||||||
|
QObject::connect(tm->control(),SIGNAL(bootstrapStatusChanged()),this,SLOT(statusChanged()));
|
||||||
|
QObject::connect(tm->control(),SIGNAL(connectivityChanged()),this,SLOT(statusChanged()));
|
||||||
|
QObject::connect(tm ,SIGNAL(errorChanged()),this,SLOT(statusChanged()));
|
||||||
|
|
||||||
|
//QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ;
|
||||||
|
|
||||||
|
mIncomingServer = new QTcpServer(this) ;
|
||||||
|
mHiddenService = NULL ;
|
||||||
|
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN;
|
||||||
|
//mBootstrapPhaseFinished = false ;
|
||||||
|
|
||||||
|
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
|
||||||
|
|
||||||
|
QTimer *timer = new QTimer ;
|
||||||
|
|
||||||
|
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
|
||||||
|
timer->start(500) ;
|
||||||
|
|
||||||
|
// Hide some debug output for the released version
|
||||||
|
|
||||||
|
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlDialog::onIncomingConnection()
|
||||||
|
{
|
||||||
|
std::cerr << "Incoming connection !!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlDialog::statusChanged()
|
||||||
|
{
|
||||||
|
int tor_control_status = mTorManager->control()->status();
|
||||||
|
int torstatus = mTorManager->control()->torStatus();
|
||||||
|
|
||||||
|
QString tor_control_status_str,torstatus_str ;
|
||||||
|
|
||||||
|
if(mTorManager->hasError())
|
||||||
|
mErrorMsg = mTorManager->errorMessage() ;
|
||||||
|
|
||||||
|
switch(tor_control_status)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Tor::TorControl::Error : tor_control_status_str = "Error" ; break ;
|
||||||
|
case Tor::TorControl::NotConnected: tor_control_status_str = "Not connected" ; break ;
|
||||||
|
case Tor::TorControl::Connecting: tor_control_status_str = "Connecting" ; break ;
|
||||||
|
case Tor::TorControl::Authenticating: tor_control_status_str = "Authenticating" ; break ;
|
||||||
|
case Tor::TorControl::Connected: tor_control_status_str = "Connected" ; break ;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(torstatus)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
|
||||||
|
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
|
||||||
|
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
|
||||||
|
}
|
||||||
|
|
||||||
|
torStatus_LB->setText(torstatus_str) ;
|
||||||
|
|
||||||
|
if(torstatus == Tor::TorControl::TorUnknown)
|
||||||
|
torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ;
|
||||||
|
else
|
||||||
|
torStatus_LB->setToolTip("") ;
|
||||||
|
|
||||||
|
QVariantMap qvm = mTorManager->control()->bootstrapStatus();
|
||||||
|
QString bootstrapstatus_str ;
|
||||||
|
|
||||||
|
std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl;
|
||||||
|
std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl;
|
||||||
|
|
||||||
|
std::cerr << "Bootstrap status map: " << std::endl;
|
||||||
|
|
||||||
|
for(auto it(qvm.begin());it!=qvm.end();++it)
|
||||||
|
std::cerr << " " << it.key().toStdString() << " : " << it.value().toString().toStdString() << std::endl;
|
||||||
|
|
||||||
|
if(!qvm["progress"].toString().isNull())
|
||||||
|
torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ;
|
||||||
|
else
|
||||||
|
torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ;
|
||||||
|
|
||||||
|
QString service_id ;
|
||||||
|
QString onion_address ;
|
||||||
|
QHostAddress service_target_address ;
|
||||||
|
uint16_t service_port ;
|
||||||
|
uint16_t target_port ;
|
||||||
|
|
||||||
|
if(mTorManager->getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
|
||||||
|
{
|
||||||
|
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port));
|
||||||
|
onionAddress_LB->setText(onion_address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hiddenServiceAddress_LB->setText(QString("[Not ready]")) ;
|
||||||
|
onionAddress_LB->setText(QString("[Not ready]")) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
showLog();
|
||||||
|
adjustSize();
|
||||||
|
|
||||||
|
QCoreApplication::processEvents(); // forces update
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorControlDialog::showLog()
|
||||||
|
{
|
||||||
|
static std::set<QString> already_seen ;
|
||||||
|
|
||||||
|
QString s ;
|
||||||
|
QStringList logmsgs = mTorManager->logMessages() ;
|
||||||
|
bool can_print = false ;
|
||||||
|
|
||||||
|
for(QStringList::const_iterator it(logmsgs.begin());it!=logmsgs.end();++it)
|
||||||
|
{
|
||||||
|
s += *it + "\n" ;
|
||||||
|
|
||||||
|
if(already_seen.find(*it) == already_seen.end())
|
||||||
|
{
|
||||||
|
can_print = true ;
|
||||||
|
already_seen.insert(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(can_print)
|
||||||
|
std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// torLog_TB->setText(s) ;:
|
||||||
|
|
||||||
|
std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
|
||||||
|
{
|
||||||
|
if(!mErrorMsg.isNull())
|
||||||
|
{
|
||||||
|
error_msg = mErrorMsg ;
|
||||||
|
return TorControlDialog::TOR_STATUS_FAIL ;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(mTorManager->control()->torStatus())
|
||||||
|
{
|
||||||
|
case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
|
||||||
|
default:
|
||||||
|
return TOR_STATUS_UNKNOWN ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService()
|
||||||
|
{
|
||||||
|
std::cerr << "Checking for hidden services:" ;
|
||||||
|
|
||||||
|
switch(mHiddenServiceStatus)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case HIDDEN_SERVICE_STATUS_UNKNOWN: {
|
||||||
|
|
||||||
|
std::cerr << " trying to setup. " ;
|
||||||
|
|
||||||
|
if(!mTorManager->setupHiddenService())
|
||||||
|
{
|
||||||
|
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_FAIL ;
|
||||||
|
std::cerr << "Failed." << std::endl;
|
||||||
|
return mHiddenServiceStatus ;
|
||||||
|
}
|
||||||
|
std::cerr << "Done." << std::endl;
|
||||||
|
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_REQUESTED ;
|
||||||
|
return mHiddenServiceStatus ;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HIDDEN_SERVICE_STATUS_REQUESTED: {
|
||||||
|
QList<Tor::HiddenService*> hidden_services = mTorManager->control()->hiddenServices();
|
||||||
|
|
||||||
|
if(hidden_services.empty())
|
||||||
|
{
|
||||||
|
std::cerr << "Not ready yet." << std::endl;
|
||||||
|
return mHiddenServiceStatus ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mHiddenService == NULL)
|
||||||
|
mHiddenService = *(hidden_services.begin()) ;
|
||||||
|
|
||||||
|
Tor::HiddenService::Status hss = mHiddenService->status();
|
||||||
|
|
||||||
|
std::cerr << "New service acquired. Status is " << hss ;
|
||||||
|
|
||||||
|
if(hss == Tor::HiddenService::Online)
|
||||||
|
{
|
||||||
|
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_OK ;
|
||||||
|
std::cerr << ": published and running!" << std::endl;
|
||||||
|
|
||||||
|
return mHiddenServiceStatus ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << ": not ready yet." << std::endl;
|
||||||
|
return mHiddenServiceStatus ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case HIDDEN_SERVICE_STATUS_OK :
|
||||||
|
std::cerr << "New service acquired." << std::endl;
|
||||||
|
return mHiddenServiceStatus ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
48
retroshare-gui/src/gui/TorControl/TorControlWindow.h
Normal file
48
retroshare-gui/src/gui/TorControl/TorControlWindow.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "ui_TorControlWindow.h"
|
||||||
|
|
||||||
|
class QTcpServer ;
|
||||||
|
|
||||||
|
namespace Tor {
|
||||||
|
class HiddenService ;
|
||||||
|
class TorManager ;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TorControlDialog: public QWidget, public Ui::TorControlDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL);
|
||||||
|
|
||||||
|
enum TorStatus {
|
||||||
|
TOR_STATUS_UNKNOWN = 0x00,
|
||||||
|
TOR_STATUS_OK = 0x01,
|
||||||
|
TOR_STATUS_FAIL = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HiddenServiceStatus {
|
||||||
|
HIDDEN_SERVICE_STATUS_UNKNOWN = 0x00, // no information known.
|
||||||
|
HIDDEN_SERVICE_STATUS_FAIL = 0x01, // some error occurred
|
||||||
|
HIDDEN_SERVICE_STATUS_REQUESTED = 0x02, // one service at least has been requested. Still being tested.
|
||||||
|
HIDDEN_SERVICE_STATUS_OK = 0x03 // one service responds and has been tested
|
||||||
|
};
|
||||||
|
|
||||||
|
// Should be called multiple times in a loop until it returns something else than *_UNKNOWN
|
||||||
|
|
||||||
|
TorStatus checkForTor(QString& error_msg) ;
|
||||||
|
HiddenServiceStatus checkForHiddenService() ;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void showLog();
|
||||||
|
void statusChanged();
|
||||||
|
void onIncomingConnection();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mErrorMsg ;
|
||||||
|
HiddenServiceStatus mHiddenServiceStatus ;
|
||||||
|
|
||||||
|
Tor::TorManager *mTorManager ;
|
||||||
|
Tor::HiddenService *mHiddenService ;
|
||||||
|
|
||||||
|
QTcpServer *mIncomingServer ;
|
||||||
|
};
|
134
retroshare-gui/src/gui/TorControl/TorControlWindow.ui
Normal file
134
retroshare-gui/src/gui/TorControl/TorControlWindow.ui
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>TorControlDialog</class>
|
||||||
|
<widget class="QWidget" name="TorControlDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>228</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Setting up Tor...</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="../gui/icons.qrc">:/icons/tor-logo.png</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="torStatusTxt_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Tor status:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="torStatus_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Unknown</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="torBootstrapStatus_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Not started</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="HiddenServiceAddressTxt_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hidden service address:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="torBootstrapStatusTxt_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Tor bootstrap status:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="hiddenServiceAddress_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Not set</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="onionAddressTxt_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Onion address:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="onionAddress_LB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Not set</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../gui/icons.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -28,11 +28,10 @@
|
|||||||
#include "retroshare/rsconfig.h"
|
#include "retroshare/rsconfig.h"
|
||||||
#include "retroshare/rsinit.h"
|
#include "retroshare/rsinit.h"
|
||||||
#include "retroshare/rspeers.h"
|
#include "retroshare/rspeers.h"
|
||||||
|
#include "retroshare/rstor.h"
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include "util/misc.h"
|
#include "util/misc.h"
|
||||||
|
|
||||||
#include "TorControl/TorManager.h"
|
|
||||||
#include "TorControl/TorControl.h"
|
|
||||||
#include "gui/common/FilesDefs.h"
|
#include "gui/common/FilesDefs.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@ -92,8 +91,8 @@ void TorStatus::getTorStatus()
|
|||||||
if(RsAccounts::isTorAuto())
|
if(RsAccounts::isTorAuto())
|
||||||
{
|
{
|
||||||
// get Tor status
|
// get Tor status
|
||||||
int tor_control_status = Tor::TorManager::instance()->control()->status();
|
RsTorConnectivityStatus tor_control_status = RsTor::torConnectivityStatus();
|
||||||
int torstatus = Tor::TorManager::instance()->control()->torStatus();
|
RsTorStatus torstatus = RsTor::torStatus();
|
||||||
|
|
||||||
QString tor_control_status_str,torstatus_str ;
|
QString tor_control_status_str,torstatus_str ;
|
||||||
bool tor_control_ok ;
|
bool tor_control_ok ;
|
||||||
@ -101,30 +100,31 @@ void TorStatus::getTorStatus()
|
|||||||
switch(tor_control_status)
|
switch(tor_control_status)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case Tor::TorControl::Error : tor_control_ok = false ; tor_control_status_str = "Error" ; break ;
|
case RsTorConnectivityStatus::ERROR : tor_control_ok = false ; tor_control_status_str = "Error" ; break ;
|
||||||
case Tor::TorControl::NotConnected: tor_control_ok = false ; tor_control_status_str = "Not connected" ; break ;
|
case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_ok = false ; tor_control_status_str = "Not connected" ; break ;
|
||||||
case Tor::TorControl::Connecting: tor_control_ok = false ; tor_control_status_str = "Connecting" ; break ;
|
case RsTorConnectivityStatus::CONNECTING: tor_control_ok = false ; tor_control_status_str = "Connecting" ; break ;
|
||||||
case Tor::TorControl::Authenticating: tor_control_ok = false ; tor_control_status_str = "Authenticating" ; break ;
|
case RsTorConnectivityStatus::AUTHENTICATING: tor_control_ok = false ; tor_control_status_str = "Authenticating" ; break ;
|
||||||
case Tor::TorControl::Connected: tor_control_ok = true ; tor_control_status_str = "Connected" ; break ;
|
case RsTorConnectivityStatus::AUTHENTICATED: tor_control_ok = false ; tor_control_status_str = "Connected" ; break ;
|
||||||
}
|
case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_ok = true ; tor_control_status_str = "Hidden service ready" ; break ;
|
||||||
|
}
|
||||||
|
|
||||||
switch(torstatus)
|
switch(torstatus)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
|
case RsTorStatus::UNKNOWN: torstatus_str = "Unknown" ; break ;
|
||||||
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
|
case RsTorStatus::OFFLINE: torstatus_str = "Tor offline" ; break ;
|
||||||
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
|
case RsTorStatus::READY: torstatus_str = "Tor ready" ; break ;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MIN_RS_NET_SIZE 10
|
#define MIN_RS_NET_SIZE 10
|
||||||
|
|
||||||
if(torstatus == Tor::TorControl::TorOffline || !online || !tor_control_ok)
|
if(torstatus == RsTorStatus::OFFLINE || !online || !tor_control_ok)
|
||||||
{
|
{
|
||||||
// RED - some issue.
|
// RED - some issue.
|
||||||
torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-stopping.png").scaledToHeight(1.5*S,Qt::SmoothTransformation));
|
torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-stopping.png").scaledToHeight(1.5*S,Qt::SmoothTransformation));
|
||||||
torstatusLabel->setToolTip( text + tr("Tor is currently offline"));
|
torstatusLabel->setToolTip( text + tr("Tor is currently offline"));
|
||||||
}
|
}
|
||||||
else if(torstatus == Tor::TorControl::TorReady && online && tor_control_ok)
|
else if(torstatus == RsTorStatus::READY && online && tor_control_ok)
|
||||||
{
|
{
|
||||||
torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation));
|
torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation));
|
||||||
torstatusLabel->setToolTip( text + tr("Tor is OK"));
|
torstatusLabel->setToolTip( text + tr("Tor is OK"));
|
||||||
@ -141,12 +141,12 @@ void TorStatus::getTorStatus()
|
|||||||
if(!_updated)
|
if(!_updated)
|
||||||
{
|
{
|
||||||
RsPeerDetails pd;
|
RsPeerDetails pd;
|
||||||
uint32_t hiddentype;
|
uint32_t hiddentype = RS_HIDDEN_TYPE_UNKNOWN;
|
||||||
if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd)) {
|
|
||||||
|
if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd))
|
||||||
|
{
|
||||||
if(pd.netMode == RS_NETMODE_HIDDEN)
|
if(pd.netMode == RS_NETMODE_HIDDEN)
|
||||||
{
|
|
||||||
hiddentype = pd.hiddenType;
|
hiddentype = pd.hiddenType;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
std::string proxyaddr;
|
std::string proxyaddr;
|
||||||
uint16_t proxyport;
|
uint16_t proxyport;
|
||||||
|
@ -66,7 +66,7 @@ CrashStackTrace gCrashStackTrace;
|
|||||||
# include "gui/settings/JsonApiPage.h"
|
# include "gui/settings/JsonApiPage.h"
|
||||||
#endif // RS_JSONAPI
|
#endif // RS_JSONAPI
|
||||||
|
|
||||||
#include "TorControl/TorManager.h"
|
#include "retroshare/rstor.h"
|
||||||
#include "TorControl/TorControlWindow.h"
|
#include "TorControl/TorControlWindow.h"
|
||||||
|
|
||||||
#include "retroshare/rsidentity.h"
|
#include "retroshare/rsidentity.h"
|
||||||
@ -395,24 +395,23 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||||||
{
|
{
|
||||||
// Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service
|
// Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service
|
||||||
|
|
||||||
QString tor_hidden_service_dir = QString::fromStdString(RsAccounts::AccountDirectory()) + QString("/hidden_service/") ;
|
std::string tor_hidden_service_dir = RsAccounts::AccountDirectory() + "/hidden_service/" ;
|
||||||
|
|
||||||
Tor::TorManager *torManager = Tor::TorManager::instance();
|
RsTor::setTorDataDirectory(Rshare::dataDirectory().toStdString() + "/tor/");
|
||||||
torManager->setTorDataDirectory(Rshare::dataDirectory() + QString("/tor/"));
|
RsTor::setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run
|
||||||
torManager->setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run
|
|
||||||
|
|
||||||
RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir.toUtf8())) ;
|
RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir)) ;
|
||||||
|
|
||||||
torManager->setupHiddenService();
|
//RsTor::setupHiddenService();
|
||||||
|
|
||||||
if(! torManager->start() || torManager->hasError())
|
if(! RsTor::start() || RsTor::hasError())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+torManager->errorMessage()) ;
|
QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+QString::fromStdString(RsTor::errorMessage())) ;
|
||||||
return 1 ;
|
return 1 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TorControlDialog tcd(torManager) ;
|
TorControlDialog tcd;
|
||||||
QString error_msg ;
|
QString error_msg ;
|
||||||
tcd.show();
|
tcd.show();
|
||||||
|
|
||||||
@ -456,30 +455,29 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||||||
{
|
{
|
||||||
// Tor works with viable hidden service. Let's use it!
|
// Tor works with viable hidden service. Let's use it!
|
||||||
|
|
||||||
QString service_id ;
|
std::string service_id ;
|
||||||
QString onion_address ;
|
std::string onion_address ;
|
||||||
uint16_t service_port ;
|
uint16_t service_port ;
|
||||||
uint16_t service_target_port ;
|
uint16_t service_target_port ;
|
||||||
uint16_t proxy_server_port ;
|
uint16_t proxy_server_port ;
|
||||||
QHostAddress service_target_address ;
|
std::string service_target_address ;
|
||||||
QHostAddress proxy_server_address ;
|
std::string proxy_server_address ;
|
||||||
|
|
||||||
Tor::TorManager *torManager = Tor::TorManager::instance();
|
RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port);
|
||||||
torManager->getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port);
|
RsTor::getProxyServerInfo(proxy_server_address,proxy_server_port) ;
|
||||||
torManager->getProxyServerInfo(proxy_server_address,proxy_server_port) ;
|
|
||||||
|
|
||||||
std::cerr << "Got hidden service info: " << std::endl;
|
std::cerr << "Got hidden service info: " << std::endl;
|
||||||
std::cerr << " onion address : " << onion_address.toStdString() << std::endl;
|
std::cerr << " onion address : " << onion_address << std::endl;
|
||||||
std::cerr << " service_id : " << service_id.toStdString() << std::endl;
|
std::cerr << " service_id : " << service_id << std::endl;
|
||||||
std::cerr << " service port : " << service_port << std::endl;
|
std::cerr << " service port : " << service_port << std::endl;
|
||||||
std::cerr << " target port : " << service_target_port << std::endl;
|
std::cerr << " target port : " << service_target_port << std::endl;
|
||||||
std::cerr << " target address : " << service_target_address.toString().toStdString() << std::endl;
|
std::cerr << " target address : " << service_target_address << std::endl;
|
||||||
|
|
||||||
std::cerr << "Setting proxy server to " << service_target_address.toString().toStdString() << ":" << service_target_port << std::endl;
|
std::cerr << "Setting proxy server to " << service_target_address << ":" << service_target_port << std::endl;
|
||||||
|
|
||||||
rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address.toString().toStdString(), service_target_port);
|
rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port);
|
||||||
rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address.toStdString(), service_port);
|
rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port);
|
||||||
rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address.toString().toStdString(),proxy_server_port) ;
|
rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rshare::initPlugins();
|
Rshare::initPlugins();
|
||||||
@ -593,6 +591,10 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
|
|||||||
RsGxsUpdateBroadcast::cleanup();
|
RsGxsUpdateBroadcast::cleanup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (is_auto_tor) {
|
||||||
|
RsTor::stop();
|
||||||
|
}
|
||||||
|
|
||||||
RsControl::instance()->rsGlobalShutDown();
|
RsControl::instance()->rsGlobalShutDown();
|
||||||
|
|
||||||
delete(soundManager);
|
delete(soundManager);
|
||||||
|
@ -363,46 +363,6 @@ wikipoos {
|
|||||||
|
|
||||||
################################### HEADERS & SOURCES #############################
|
################################### HEADERS & SOURCES #############################
|
||||||
|
|
||||||
# Tor controller
|
|
||||||
|
|
||||||
HEADERS += TorControl/AddOnionCommand.h \
|
|
||||||
TorControl/AuthenticateCommand.h \
|
|
||||||
TorControl/CryptoKey.h \
|
|
||||||
TorControl/GetConfCommand.h \
|
|
||||||
TorControl/HiddenService.h \
|
|
||||||
TorControl/PendingOperation.h \
|
|
||||||
TorControl/ProtocolInfoCommand.h \
|
|
||||||
TorControl/SecureRNG.h \
|
|
||||||
TorControl/SetConfCommand.h \
|
|
||||||
TorControl/Settings.h \
|
|
||||||
TorControl/StrUtil.h \
|
|
||||||
TorControl/TorControl.h \
|
|
||||||
TorControl/TorControlCommand.h \
|
|
||||||
TorControl/TorControlSocket.h \
|
|
||||||
TorControl/TorManager.h \
|
|
||||||
TorControl/TorProcess.h \
|
|
||||||
TorControl/TorProcess_p.h \
|
|
||||||
TorControl/TorSocket.h \
|
|
||||||
TorControl/Useful.h
|
|
||||||
|
|
||||||
SOURCES += TorControl/AddOnionCommand.cpp \
|
|
||||||
TorControl/AuthenticateCommand.cpp \
|
|
||||||
TorControl/GetConfCommand.cpp \
|
|
||||||
TorControl/HiddenService.cpp \
|
|
||||||
TorControl/ProtocolInfoCommand.cpp \
|
|
||||||
TorControl/SetConfCommand.cpp \
|
|
||||||
TorControl/TorControlCommand.cpp \
|
|
||||||
TorControl/TorControl.cpp \
|
|
||||||
TorControl/TorControlSocket.cpp \
|
|
||||||
TorControl/TorManager.cpp \
|
|
||||||
TorControl/TorProcess.cpp \
|
|
||||||
TorControl/TorSocket.cpp \
|
|
||||||
TorControl/CryptoKey.cpp \
|
|
||||||
TorControl/PendingOperation.cpp \
|
|
||||||
TorControl/SecureRNG.cpp \
|
|
||||||
TorControl/Settings.cpp \
|
|
||||||
TorControl/StrUtil.cpp
|
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
HEADERS += rshare.h \
|
HEADERS += rshare.h \
|
||||||
retroshare-gui/configpage.h \
|
retroshare-gui/configpage.h \
|
||||||
|
@ -29,7 +29,10 @@
|
|||||||
#include "util/stacktrace.h"
|
#include "util/stacktrace.h"
|
||||||
#include "util/argstream.h"
|
#include "util/argstream.h"
|
||||||
#include "util/rskbdinput.h"
|
#include "util/rskbdinput.h"
|
||||||
|
#include "util/rsdir.h"
|
||||||
#include "retroshare/rsinit.h"
|
#include "retroshare/rsinit.h"
|
||||||
|
#include "retroshare/rstor.h"
|
||||||
|
#include "retroshare/rspeers.h"
|
||||||
#include "retroshare/rsinit.h"
|
#include "retroshare/rsinit.h"
|
||||||
#include "retroshare/rsiface.h"
|
#include "retroshare/rsiface.h"
|
||||||
#include "util/rsdebug.h"
|
#include "util/rsdebug.h"
|
||||||
@ -44,7 +47,6 @@
|
|||||||
|
|
||||||
static CrashStackTrace gCrashStackTrace;
|
static CrashStackTrace gCrashStackTrace;
|
||||||
|
|
||||||
|
|
||||||
#ifdef RS_SERVICE_TERMINAL_LOGIN
|
#ifdef RS_SERVICE_TERMINAL_LOGIN
|
||||||
class RsServiceNotify: public NotifyClient
|
class RsServiceNotify: public NotifyClient
|
||||||
{
|
{
|
||||||
@ -295,6 +297,35 @@ int main(int argc, char* argv[])
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
return -result;
|
return -result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(RsAccounts::isTorAuto())
|
||||||
|
{
|
||||||
|
|
||||||
|
std::cerr << "(II) Hidden service is ready:" << std::endl;
|
||||||
|
|
||||||
|
std::string service_id ;
|
||||||
|
std::string onion_address ;
|
||||||
|
uint16_t service_port ;
|
||||||
|
uint16_t service_target_port ;
|
||||||
|
uint16_t proxy_server_port ;
|
||||||
|
std::string service_target_address ;
|
||||||
|
std::string proxy_server_address ;
|
||||||
|
|
||||||
|
RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port);
|
||||||
|
RsTor::getProxyServerInfo(proxy_server_address,proxy_server_port) ;
|
||||||
|
|
||||||
|
std::cerr << " onion address : " << onion_address << std::endl;
|
||||||
|
std::cerr << " service_id : " << service_id << std::endl;
|
||||||
|
std::cerr << " service port : " << service_port << std::endl;
|
||||||
|
std::cerr << " target port : " << service_target_port << std::endl;
|
||||||
|
std::cerr << " target address : " << service_target_address << std::endl;
|
||||||
|
|
||||||
|
std::cerr << "Setting proxy server to " << service_target_address << ":" << service_target_port << std::endl;
|
||||||
|
|
||||||
|
rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port);
|
||||||
|
rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port);
|
||||||
|
rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // def RS_SERVICE_TERMINAL_LOGIN
|
#endif // def RS_SERVICE_TERMINAL_LOGIN
|
||||||
|
|
||||||
|
@ -22,8 +22,6 @@
|
|||||||
|
|
||||||
TARGET = retroshare-service
|
TARGET = retroshare-service
|
||||||
|
|
||||||
CONFIG -= qt
|
|
||||||
|
|
||||||
!include("../../libretroshare/src/use_libretroshare.pri"):error("Including")
|
!include("../../libretroshare/src/use_libretroshare.pri"):error("Including")
|
||||||
|
|
||||||
SOURCES += retroshare-service.cc
|
SOURCES += retroshare-service.cc
|
||||||
|
Loading…
Reference in New Issue
Block a user