1 /* 2 SPDX-License-Identifier: GPL-2.0-or-later 3 4 SPDX-FileCopyrightText: 2007 Shintaro Matsuoka <shin@shoegazed.org> 5 SPDX-FileCopyrightText: 2009 Bernd Buschinski <b.buschinski@web.de> 6 */ 7 8 #include "dcccommon.h" 9 10 #include "preferences.h" 11 #include "server.h" 12 #include "konversation_log.h" 13 14 #include <config-konversation.h> 15 16 #include <cstdlib> 17 #include <sys/types.h> 18 #ifndef Q_OS_WIN 19 # include <netinet/in.h> 20 # include <sys/socket.h> 21 # include <net/if.h> 22 # include <sys/ioctl.h> 23 # ifdef HAVE_STROPTS_H 24 # include <stropts.h> 25 # endif 26 # include <arpa/inet.h> 27 #endif 28 29 #include <QHostAddress> 30 #include <QTcpServer> 31 32 33 namespace Konversation 34 { 35 namespace DCC 36 { 37 //TODO: IPv6 support, CHECK ME textIpToNumericalIp(const QString & ipString)38 QString DccCommon::textIpToNumericalIp( const QString& ipString ) 39 { 40 QHostAddress ip; 41 ip.setAddress( ipString ); 42 switch (ip.protocol()) 43 { 44 case QAbstractSocket::IPv4Protocol: 45 return QString::number( ip.toIPv4Address() ); 46 47 case QAbstractSocket::IPv6Protocol: 48 //ipv6 is not numerical, it is just normal text, "0:c00:0:0:1f::" for example 49 return ip.toString(); 50 51 default: 52 qCDebug(KONVERSATION_LOG) << "unsupported protocol: " << ipString; 53 return QString(); 54 } 55 } 56 numericalIpToTextIp(const QString & numericalIp)57 QString DccCommon::numericalIpToTextIp( const QString& numericalIp ) 58 { 59 QHostAddress ip; 60 61 //Only IPV6 can contain ':' 62 if (numericalIp.contains(QLatin1Char(':'))) { 63 return numericalIp; 64 } 65 //ipv4 comes as numericalip 66 else 67 { 68 ip.setAddress( numericalIp.toULong() ); 69 } 70 71 return ip.toString(); 72 } 73 getOwnIp(Server * server)74 QString DccCommon::getOwnIp( Server* server ) 75 { 76 QString ownIp; 77 int methodId = Preferences::self()->dccMethodToGetOwnIp(); 78 79 if ( methodId == 1 && server ) 80 { 81 // by the WELCOME message or the USERHOST message from the server 82 ownIp = server->getOwnIpByServerMessage(); 83 } 84 else if ( methodId == 2 && !Preferences::self()->dccSpecificOwnIp().isEmpty() ) 85 { 86 // manual 87 QHostInfo res = QHostInfo::fromName(Preferences::self()->dccSpecificOwnIp()); 88 if(res.error() == QHostInfo::NoError && !res.addresses().isEmpty()) 89 { 90 ownIp = res.addresses().first().toString(); 91 } 92 } 93 94 // fallback or methodId == 0 (network interface) 95 if ( ownIp.isEmpty() && server ) 96 { 97 ownIp = server->getOwnIpByNetworkInterface(); 98 } 99 100 qCDebug(KONVERSATION_LOG) << ownIp; 101 return ownIp; 102 } 103 ipv6FallbackAddress(const QString & address)104 QString DccCommon::ipv6FallbackAddress(const QString& address) 105 { 106 QString fallbackIp = address; 107 QHostAddress ip(address); 108 if (ip.protocol() == QAbstractSocket::IPv6Protocol) 109 { 110 #ifndef Q_OS_WIN 111 /* This is fucking ugly but there is no KDE way to do this yet :| -cartman */ 112 struct ifreq ifr; 113 const QByteArray addressBa = Preferences::self()->dccIPv4FallbackIface().toLatin1(); 114 const char* address = addressBa.constData(); 115 int sock = socket(AF_INET, SOCK_DGRAM, 0); 116 strncpy(ifr.ifr_name, address, IF_NAMESIZE - 1); 117 ifr.ifr_name[IF_NAMESIZE - 1] = '\0'; 118 ifr.ifr_addr.sa_family = AF_INET; 119 120 if (ioctl( sock, SIOCGIFADDR, &ifr ) >= 0) 121 { 122 struct sockaddr_in sock; 123 memcpy(&sock, &ifr.ifr_addr, sizeof(ifr.ifr_addr)); 124 fallbackIp = QString::fromLatin1(inet_ntoa(sock.sin_addr)); 125 } 126 qCDebug(KONVERSATION_LOG) << "Falling back to IPv4 address " << fallbackIp; 127 #else 128 qCDebug(KONVERSATION_LOG) << "TODO: implement ipv6 fallback"; 129 #endif 130 } 131 return fallbackIp; 132 } 133 createServerSocketAndListen(QObject * parent,QString * failedReason,int minPort,int maxPort)134 QTcpServer* DccCommon::createServerSocketAndListen( QObject* parent, QString* failedReason, int minPort, int maxPort ) 135 { 136 auto* socket = new QTcpServer( parent ); 137 138 if ( minPort > 0 && maxPort >= minPort ) // ports are configured manually 139 { 140 // set port 141 bool found = false; // whether succeeded to set port 142 for ( int port = minPort; port <= maxPort ; ++port ) 143 { 144 bool success = socket->listen( QHostAddress::Any, port ); 145 if ( ( found = ( success && socket->isListening() ) ) ) 146 break; 147 socket->close(); 148 } 149 if ( !found ) 150 { 151 if ( failedReason ) 152 *failedReason = i18n( "No vacant port" ); 153 delete socket; 154 return nullptr; 155 } 156 } 157 else 158 { 159 // Let the operating system choose a port 160 if ( !socket->listen() ) 161 { 162 if ( failedReason ) 163 *failedReason = i18n( "Could not open a socket" ); 164 delete socket; 165 return nullptr; 166 } 167 } 168 169 return socket; 170 } 171 } 172 } 173