コード例 #1
0
void UdpRelay::onServerUdpSocketReadyRead()
{
    if (listenSocket.pendingDatagramSize() > RecvSize) {
        emit info("[UDP] Datagram is too large. discarded.");
        return;
    }

    QByteArray data;
    data.resize(listenSocket.pendingDatagramSize());
    QHostAddress r_addr;
    quint16 r_port;
    qint64 readSize = listenSocket.readDatagram(data.data(),
                                                RecvSize,
                                                &r_addr,
                                                &r_port);
    emit bytesRead(readSize);

    if (isLocal) {
        if (static_cast<int>(data[2]) != 0) {
            emit info("[UDP] Drop a message since frag is not 0");
            return;
        }
        data.remove(0, 3);
    } else {
        if (autoBan && Common::isAddressBanned(r_addr)) {
            emit debug(QString("[UDP] A banned IP %1 "
                               "attempted to access this server")
                       .arg(r_addr.toString()));
            return;
        }
        data = encryptor->decryptAll(data);
    }

    Address destAddr, remoteAddr(r_addr, r_port);//remote == client
    int header_length = 0;
    bool at_auth = false;
    Common::parseHeader(data, destAddr, header_length, at_auth);
    if (header_length == 0) {
        emit info("[UDP] Can't parse header. "
                  "Wrong encryption method or password?");
        if (!isLocal && autoBan) {
            Common::banAddress(r_addr);
        }
        return;
    }

    QUdpSocket *client = cache.value(remoteAddr, nullptr);
    QString dbg;
    if (!client) {
        client = new QUdpSocket(this);
        client->setReadBufferSize(RecvSize);
        client->setSocketOption(QAbstractSocket::LowDelayOption, 1);
        cache.insert(remoteAddr, client);
        connect(client, &QUdpSocket::readyRead,
                this, &UdpRelay::onClientUdpSocketReadyRead);
        connect(client, &QUdpSocket::disconnected,
                this, &UdpRelay::onClientDisconnected);
        QDebug(&dbg) << "[UDP] cache miss:" << destAddr << "<->" << remoteAddr;
    } else {
        QDebug(&dbg) << "[UDP] cache hit:" << destAddr << "<->" << remoteAddr;
    }
    emit debug(dbg);

    if (isLocal) {
        if (auth || at_auth) {
            /*
             * shadowsocks UDP Request (OTA-enabled, unencrypted)
             * +------+----------+----------+----------+-------------+
             * | ATYP | DST.ADDR | DST.PORT |   DATA   |  HMAC-SHA1  |
             * +------+----------+----------+----------+-------------+
             * |  1   | Variable |    2     | Variable |     10      |
             * +------+----------+----------+----------+-------------+
             */
            encryptor->addHeaderAuth(data);
        }
        data = encryptor->encryptAll(data);
        destAddr = serverAddress;
    } else {
        if (auth || at_auth) {
            if (!encryptor->verifyHeaderAuth(data,
                                             data.length() - Cipher::AUTH_LEN))
            {
                emit info("[UDP] One-time message authentication "
                          "for header failed.");
                if (autoBan) {
                    Common::banAddress(r_addr);
                }
                return;
            }
        }
        data = data.mid(header_length,
                        data.length() - header_length - Cipher::AUTH_LEN);
    }

    if (!destAddr.isIPValid()) {//TODO async dns
        destAddr.blockingLookUp();
    }
    client->writeDatagram(data, destAddr.getFirstIP(), destAddr.getPort());
}