1

My main issue is: I have a list of recognised IPs and I am carrying out Multi threaded TCP Client-Server Communication; so whenever a new connection request comes from any random client (server is constantly listening), I want to first compare that IP to the stored IP's and allow a new connection only if it is one of my recognised IP's. There can be a .txt file on hard disk or a QList or QString whichever is a better soluion.

EDIT: To make myself clear, Following is the server.cpp file I've developed so far and the errors I am confronted with at the moment.

#include "myserver.h"
#include "ioprogram.h"
#include <string>
#include <iostream>

using namespace std;

//string ClientInfo;

MyServer::MyServer(QObject *parent): QTcpServer(parent)
{
    QStringList accepted_ip_list;   //List of remote IPs that can be accepted in QString list format
    accepted_ip_list.append("127.0.0.1");   // IPv4 local address
    accepted_ip_list.append("::1");         // IPv6 local address

    // Convert from QString to integer format, generating new list
    foreach (const QString &ip, accepted_ip_list)
    {
        QHostAddress host_address(ip);
        my_accepted_ip_list.append(host_address);
    }

    myserver = new QTcpServer(this);

    connect(myserver, &QTcpServer::incomingConnection, this, &MyServer::incomingConnection);

    myserver->listen(QHostAddress::Any, 1234);
}

void MyServer::startServer()
{
    if(!this->listen(QHostAddress::Any,1234))
    {
        qDebug() << "Could not start server.";
    }
    else
    {
        qDebug() << "Listening...";
    }
}

void MyServer::incomingConnection(qintptr socketDescriptor)
{
    qDebug() << socketDescriptor << "Connecting...";

    while (myserver->hasPendingConnections())
    {
        QTcpSocket *socket = myserver->nextPendingConnection();
        QHostAddress host_address = socket->peerAddress();

        bool contains = false;

        for(int i=0; i < my_accepted_ip_list.size(); i++)
        {
            if(my_accepted_ip_list[i].isEqual(host_address,QHostAddress::ConvertV4MappedToIPv4))
            {
                contains = true;
                break;
            }
        }

        if(contains)
        {
            MyThread *thread = new MyThread(socketDescriptor, this);

            connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
            thread->start();
        }
        else
        {
            socket->abort();        // Reject peer by disconnecting it
            socket->deleteLater();  // Schedule the socket removal from memory
        }
    }
}

And here are the errors:

1) @line10 --- prototype for 'MyServer::MyServer(QObject*)' does not match any in class 'MyServer'

2) @line55 --- class 'QHostAddress' has no member named 'isEqual'

3) @line55 --- 'ConvertV4MappedToIPv4' is not a member of 'QHostAddress'

and here is the header file:

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include <QDebug>
#include "mythread.h"
//#include "ioprogram.h"

class MyServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyServer(QTcpServer *parent = nullptr);
    void startServer();

signals:

private slots:
//    void newConnection();

private:
    QTcpServer *myserver;
    QList<QHostAddress> my_accepted_ip_list;    //List of IPv4 addresses allowed by the server, in quint32 not QString

protected:
    void incomingConnection(qintptr socketDescriptor);
};
#endif // MYSERVER_H

and here is one error in the header file: candidates are: MyServer::MyServer(MyServer&&)

Shahwani
  • 15
  • 8

1 Answers1

1

If i'm correct, you're looking for peerAddress with gives to you a QHostAddress.

Simple example of how to accept and reject peers:

EDIT: Since you're using the IP as a security resource, i suggest you use QSslSocket for encryption and authenticity. And contact some security expert, that i am not ;)

EDIT2: Added support for IPv6 comparison.

EDIT3: Modified method of comparison.

Header example (myserver.h file):

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>

class MyServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyServer(QObject *parent = nullptr);

    void startServer();

private:
    QList<QHostAddress> my_accepted_ip_list;    //List of addresses allowed by the server, in QHostAddress not QString

protected:
    void incomingConnection(qintptr socketDescriptor);
};

#endif // MYSERVER_H

CPP file example (myserver.cpp file):

#include "myserver.h"

MyServer::MyServer(QObject *parent) : QTcpServer(parent)
{
    QStringList accepted_ip_list;   //List of remote IPs that can be accepted in QString list format
    accepted_ip_list.append("127.0.0.1");   // IPv4 local address
    accepted_ip_list.append("::1");         // IPv6 local address

    // Convert from QString to QHostAddress format, generating new list
    foreach (const QString &ip, accepted_ip_list)
    {
        QHostAddress host_address(ip);
        my_accepted_ip_list.append(host_address);
    }
}

void MyServer::startServer()
{
    if (!listen(QHostAddress::Any, 1234))
    {
        qDebug() << "Could not start server.";
    }
    else
    {
        qDebug() << "Listening...";
    }
}

void MyServer::incomingConnection(qintptr socketDescriptor)
{
    QTcpSocket *socket = new QTcpSocket(this);
    socket->setSocketDescriptor(socketDescriptor);

    QHostAddress host_address = socket->peerAddress();

    quint32 ipv4 = host_address.toIPv4Address();

    QByteArray ipv6 = QByteArray((char*)host_address.toIPv6Address().c, 16);

    bool contains = false;

    for (int i = 0; i < my_accepted_ip_list.size(); i++)
    {
        quint32 accepted_ipv4 = my_accepted_ip_list[i].toIPv4Address();
        QByteArray accepted_ipv6 = QByteArray((char*)my_accepted_ip_list[i].toIPv6Address().c, 16);

        if (accepted_ipv4 == ipv4 || accepted_ipv6 == ipv6)
        {
            contains = true;
            break;
        }
    }

    if (contains)
    {
        qDebug() << qPrintable(socket->peerAddress().toString()) << "Accepted";
    }
    else
    {
        qDebug() << qPrintable(socket->peerAddress().toString()) << "Rejected";
        socket->abort();        // Reject peer by disconnecting it
        socket->deleteLater();  // Schedule the socket removal from memory
    }
}
Antonio Dias
  • 2,751
  • 20
  • 40
  • I understood your code and I believe it'll work in my program but I am unable to initialize a _m_server_ instance; can you please advise. – Shahwani Aug 06 '17 at 21:57
  • Thank you so much for the help, but unfortunately I am still not able to run my program. It gives an error while compiling: type QObject is not a direct base of 'MyServer' – Shahwani Aug 07 '17 at 10:09
  • Please refer to my post that now has the code and the errors that am trying to solve. Thanks :) – Shahwani Aug 08 '17 at 14:00
  • @Shahwani: I've made an edit based on your code, please see. The problems with `QHostAddress` maybe caused by use of a version of Qt prior to Qt 5.8. See [here](https://doc.qt.io/qt-5/qhostaddress.html#isEqual). – Antonio Dias Aug 08 '17 at 16:55
  • Thanks a lot! Now my code is running without errors. But just one issue still: I used another laptop and append its IP by this command: `accepted_ip_list.append("172.18.48.139");` but still the server is rejecting that connection; I used `qDebug() << "Rejected";` to see if there is a rejection! – Shahwani Aug 09 '17 at 13:11
  • @Shahwani: Are you sure that you added the right IP to the accepted list? I've edited my answer to demonstrate a simple way to see who is accepted and rejected. – Antonio Dias Aug 10 '17 at 09:32
  • A thought came to my mind while reading a topic at SO: https://stackoverflow.com/questions/13199267/authenticating-users-on-a-qt-server What if instead of checking IPs and keeping a log, we just use username: password approach. If a client is authorised it will know the password to connect to the server and clients will be distinguished by their usernames so no need of matching the IPs and Port numbers! – Shahwani Aug 20 '17 at 16:48
  • I believe that is the correct approach! Consider use `QSslSocket` for encryption and authentication of the server before transfer the username and password. Also look info for Qt Databases to securely store username and password! But I believe you may consider ask a new question here on Stack Overflow so anyone will'be able to help you better! :) – Antonio Dias Aug 20 '17 at 22:45
  • I noticed in your code that you converted each QString into integer inside the foreach loop. Let me know what is the purpose of doing that. Can't we just fetch the IP of the remote host, convert it into a **string** and compare that to our _registered IP list_ already stored in string format in the text file? – Shahwani Aug 24 '17 at 11:53
  • Sorry for late response! I'm converting to `quint32` (in IPv4) just to provide faster comparision its only 4 bytes, instead of the bytes of the respective `QString`. – Antonio Dias Aug 27 '17 at 02:38