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