PageRenderTime 81ms CodeModel.GetById 2ms app.highlight 73ms RepoModel.GetById 1ms app.codeStats 0ms

/legacy/libdissent/network.cc

https://github.com/brainburn/Dissent
C++ | 662 lines | 511 code | 94 blank | 57 comment | 93 complexity | 5d621598c12dc698ea783a2e3abd642d MD5 | raw file
  1/* libdissent/network.cc
  2   Network layer (w/ signing and logging) for dissent protocol.
  3
  4   Author: Shu-Chun Weng <scweng _AT_ cs .DOT. yale *DOT* edu>
  5 */
  6/* ====================================================================
  7 * Dissent: Accountable Group Anonymity
  8 * Copyright (c) 2010 Yale University.
  9 *
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation; either
 13 * version 2.1 of the License, or (at your option) any later version.
 14 *
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 *
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to
 22 *
 23 *   Free Software Foundation, Inc.,
 24 *   51 Franklin Street, Fifth Floor,
 25 *   Boston, MA  02110-1301  USA
 26 */
 27
 28#include "network.hpp"
 29
 30#include <QtGlobal>
 31#include <QAbstractSocket>
 32#include <QCoreApplication>
 33#include <QHostAddress>
 34#include <QHostInfo>
 35#include <QSet>
 36#include <QSignalMapper>
 37#include <QTcpServer>
 38#include <QTcpSocket>
 39#include <QTimer>
 40#include <QVariant>
 41#include <cstdio>
 42
 43#include "QByteArrayUtil.hpp"
 44#include "config.hpp"
 45#include "crypto.hpp"
 46#include "random_util.hpp"
 47
 48namespace Dissent{
 49const int Network::MulticastNodeId;
 50
 51Network::Network(Configuration* config)
 52    : _config(config),
 53      _multicast(0),
 54      _isReady(false),
 55      _inReceivingPhase(false),
 56      _nonce(-1){
 57    _prepare = new NetworkPrepare(config, &_server, &_clients);
 58    connect(_prepare, SIGNAL(networkReady()),
 59            this, SLOT(NetworkReady()));
 60
 61    _prepare->DoPrepare(
 62            QHostAddress::Any,
 63            config->nodes[config->my_node_id].port);
 64}
 65
 66void Network::ResetSession(qint32 nonce){
 67    _nonce = nonce;
 68    ClearLog();
 69    // clear accumulative hash
 70}
 71
 72int Network::Send(int node_id, const QByteArray& data){
 73    QTcpSocket* socket = _clients[node_id];
 74    Q_ASSERT(socket);
 75    Q_ASSERT(socket->state() == QAbstractSocket::ConnectedState);
 76
 77    QByteArray plaintext, sig;
 78    PrepareMessage(LogEntry::SEND, data, &plaintext, &sig);
 79    int w_count = socket->write(plaintext);
 80    w_count += socket->write(sig);
 81    Q_ASSERT(w_count == plaintext.size() + sig.size());
 82
 83    //printf("To %d: %s\n", node_id, (char*) plaintext.toHex().data());
 84    //printf("Sig:  %s\n", (char*) sig.toHex().data());
 85    //printf("-> %s:%d\n",
 86    //       (char*) socket->peerAddress().toString().toUtf8().data(),
 87    //       socket->peerPort());
 88
 89    LogEntry log = { LogEntry::SEND, node_id, _nonce, data, sig, true };
 90    _log.push_back(log);
 91    return w_count;
 92}
 93
 94int Network::Broadcast(const QByteArray& data){
 95    QByteArray plaintext, sig;
 96    PrepareMessage(LogEntry::BROADCAST_SEND, data, &plaintext, &sig);
 97
 98    foreach(const NodeInfo& node, _config->nodes)
 99        if(node.node_id != _config->my_node_id && !node.excluded){
100            QTcpSocket* socket = _clients[node.node_id];
101            Q_ASSERT_X(socket, "Network::Broadcast",
102                       (const char*) QString("socket[%1] = null")
103                       .arg(node.node_id).toUtf8().data());
104            Q_ASSERT_X(socket->state() == QAbstractSocket::ConnectedState,
105                       "Network::Broadcast",
106                       (const char*) QString("socket[%1] wrong state")
107                       .arg(node.node_id).toUtf8().data());
108            int w_count = socket->write(plaintext);
109            w_count += socket->write(sig);
110            Q_ASSERT(w_count == plaintext.size() + sig.size());
111        }
112
113    LogEntry log = { LogEntry::BROADCAST_SEND, -1, _nonce, data, sig, true };
114    _log.push_back(log);
115    return plaintext.size() + sig.size();
116}
117
118int Network::MulticastXor(const QByteArray& data){
119    Q_ASSERT(_multicast == 0);
120    const int collector_node_id = _config->topology.front().node_id;
121    if(collector_node_id == _config->my_node_id){
122        _multicast = new MulticastXorProcessor(this, _config->num_nodes, data);
123        connect(_multicast, SIGNAL(multicastReady(QByteArray)),
124                this, SLOT(MulticastReady(QByteArray)), Qt::QueuedConnection);
125        connect(_multicast, SIGNAL(multicastError(int, QString)),
126                this, SLOT(MulticastError(int, QString)));
127        while(_multicastBuffer.size()){
128            Q_ASSERT(_multicastBuffer.front().status == Buffer::DONE);
129            const LogEntry& entry = _multicastBuffer.front().entry;
130            _multicast->EnterMessage(entry.node_id, entry.data);
131            _multicastBuffer.pop_front();
132        }
133        return data.size();
134    }
135
136    QTcpSocket* socket = _clients[collector_node_id];
137    Q_ASSERT(socket);
138    Q_ASSERT(socket->state() == QAbstractSocket::ConnectedState);
139
140    QByteArray plaintext, sig;
141    PrepareMessage(LogEntry::MULTICAST, data, &plaintext, &sig);
142    int w_count = socket->write(plaintext);
143    w_count += socket->write(sig);
144    Q_ASSERT(w_count == plaintext.size() + sig.size());
145
146    LogEntry log = { LogEntry::MULTICAST, collector_node_id,
147                     _nonce, data, sig, true };
148    _log.push_back(log);
149    return w_count;
150}
151
152int Network::Read(int node_id, QByteArray* data){
153    QList<Buffer>& buffer = _buffers[node_id];
154    while(buffer.size() > 0 && buffer.front().status == Buffer::DONE){
155        const Buffer& buf = buffer.front();
156        const bool valid = buf.entry.valid;
157        int nonce = buf.entry.nonce;
158        *data = buf.entry.data;
159        _log.push_back(buf.entry);
160        buffer.pop_front();  // now we can drop it from buffer
161        if(nonce != _nonce) fprintf(stderr, "Invalid nonce\n");
162        if(valid && nonce == _nonce)
163            return 1;
164    }
165    return 0;
166}
167
168void Network::PrepareMessage(int type, const QByteArray& data,
169                             QByteArray* message, QByteArray* sig){
170    // Updates of this function should pair up with updates of
171    // ValidateLogEntry() and ClientHasReadyRead()
172    message->clear();
173    QByteArrayUtil::AppendInt(type, message);
174    QByteArrayUtil::AppendInt(_nonce, message);
175    // TODO(scw): how to accumulate hash?
176    message->append(data);
177
178    bool r = Crypto::GetInstance()->Sign(&_config->identity_sk,
179                                         *message, sig);
180    Q_ASSERT_X(r, "Network::PrepareMessage", "message signing failed");
181
182    int message_length = message->size();
183    QByteArrayUtil::PrependInt(sig->size(), message);
184    QByteArrayUtil::PrependInt(message_length, message);
185}
186
187bool Network::ValidateLogEntry(LogEntry* entry){
188    // Updates of this function should pair up with updates of
189    // PrepareMessage() and ClientHasReadyRead()
190    bool valid_sig = Crypto::GetInstance()->Verify(
191            &_config->nodes[entry->node_id].identity_pk,
192            entry->data,
193            entry->signature);
194    entry->dir = static_cast<LogEntry::Dir>(
195            QByteArrayUtil::ExtractInt(true, &entry->data));
196    entry->nonce =
197            QByteArrayUtil::ExtractInt(true, &entry->data);
198    bool valid_dir = (entry->dir == LogEntry::SEND ||
199                      entry->dir == LogEntry::BROADCAST_SEND ||
200                      entry->dir == LogEntry::MULTICAST ||
201                      entry->dir == LogEntry::MULTICAST_FINAL);
202    if(entry->dir == LogEntry::SEND)
203        entry->dir = LogEntry::RECV;
204    else if(entry->dir == LogEntry::BROADCAST_SEND)
205        entry->dir = LogEntry::BROADCAST_RECV;
206
207    if(!valid_sig) fprintf(stderr, "Invalid signature\n");
208    if(!valid_dir) fprintf(stderr, "Invalid direction\n");
209    return entry->valid = (valid_sig && valid_dir);
210}
211
212void Network::ClientHasReadyRead(int node_id){
213    // Updates of this function should pair up with updates of
214    // PrepareMessage() and ValidateLogEntry()
215    if(_config->nodes[node_id].excluded)
216        return;
217    QMap<int, QTcpSocket*>::const_iterator it = _clients.constFind(node_id);
218    if(it == _clients.constEnd())
219        qFatal("Unknown client notifying ready");
220    QTcpSocket* socket = it.value();
221
222    QList<Buffer>& buffer = _buffers[node_id];
223    if(buffer.size() == 0 || buffer.back().status == Buffer::DONE)
224        buffer.push_back(Buffer());
225    Buffer& buf = buffer.back();
226    QByteArray byte_array;
227    switch(buf.status){
228        case Buffer::NEW:
229            if(socket->bytesAvailable() < QByteArrayUtil::IntegerSize * 2)
230                return;
231            byte_array = socket->read(QByteArrayUtil::IntegerSize * 2);
232            buf.data_len = QByteArrayUtil::ExtractInt(true, &byte_array);
233            buf.sig_len = QByteArrayUtil::ExtractInt(true, &byte_array);
234            buf.status = Buffer::HAS_SIZE;
235            // fall through
236
237        case Buffer::HAS_SIZE:
238            if(socket->bytesAvailable() < buf.data_len)
239                return;
240            buf.entry.data = socket->read(buf.data_len);
241            buf.status = Buffer::DATA_DONE;
242            // fprintf(stderr, "<%d> %s\n", node_id, (char*) buf.entry.data.toHex().data());
243            // fall through
244
245        case Buffer::DATA_DONE:
246            if(socket->bytesAvailable() < buf.sig_len)
247                return;
248            buf.entry.signature = socket->read(buf.sig_len);
249            buf.entry.node_id = node_id;
250            buf.status = Buffer::DONE;
251            // fprintf(stderr, "s%d> %s\n", node_id, (char*) buf.entry.signature.toHex().data());
252            if(!ValidateLogEntry(&buf.entry)){
253                fprintf(stderr,
254                        "Package from node %d cannot be validated\n"
255                        ">> %s\n", node_id, buf.entry.data.toHex().data());
256                break;
257            }else if(buf.entry.dir == LogEntry::MULTICAST){
258                if(_config->topology.front().node_id !=
259                        _config->my_node_id){
260                    fprintf(stderr,
261                            "multicast message from node %d to non-leader\n",
262                            node_id);
263                    break;
264                }
265
266                if(!_multicast){
267                    _multicastBuffer.push_back(buf);
268                    buffer.pop_back();
269
270                    // No consumer, log it ourselves and pop off
271                    _log.push_back(buf.entry);
272                }else{
273                    Q_ASSERT_X(_multicastBuffer.size() == 0,
274                               "Network::ClientHasReadyRead",
275                               "multicast and multicast buffer shouldn't"
276                               " coexist");
277                    LogEntry entry = buf.entry;
278                    buffer.pop_back();
279                    _multicast->EnterMessage(node_id, entry.data);
280
281                    // No consumer, log it ourselves and pop off
282                    _log.push_back(entry);
283                }
284                break;
285            }else if(buf.entry.dir == LogEntry::MULTICAST_FINAL){
286                node_id = MulticastNodeId;
287                _buffers[MulticastNodeId].push_back(buf);
288                buffer.pop_back();
289            }
290            
291            if(_inReceivingPhase){
292                emit readyRead(node_id);
293            }
294            break;
295
296        default:
297            qFatal("Invalid buf.status: %d\n", buf.status);
298            break;
299    }
300
301    // XXX(scw): change tail recursion to loop
302    if(socket->bytesAvailable() > 0)
303        ClientHasReadyRead(node_id);
304}
305
306void Network::WaitForBytesWritten(){
307    foreach(const NodeInfo& node, _config->nodes)
308        if(node.node_id != _config->my_node_id && !node.excluded){
309            QTcpSocket* socket = _clients[node.node_id];
310            Q_ASSERT_X(socket, "Network::WaitForBytesWritten",
311                       (const char*) QString("socket[%1] = null")
312                       .arg(node.node_id).toUtf8().data());
313            while(socket->state() == QAbstractSocket::ConnectedState &&
314                  socket->bytesToWrite() > 0){
315                qApp->processEvents();
316                socket->flush();
317                socket->waitForBytesWritten(-1);
318            }
319        }
320}
321
322void Network::NetworkReady(){
323    // Or keep it so that hosts can reconnect if connection dropped?
324    delete _prepare; _prepare = 0;
325
326    _signalMapper = new QSignalMapper(this);
327    connect(_signalMapper, SIGNAL(mapped(int)),
328            this, SLOT(ClientHasReadyRead(int)));
329
330    _clientNodeId.clear();
331    for(QMap<int, QTcpSocket*>::const_iterator it =  _clients.constBegin();
332        it != _clients.constEnd(); ++it){
333        _buffers.insert(it.key(), QList<Buffer>());
334        _clientNodeId.insert(it.value(), it.key());
335        _signalMapper->setMapping(it.value(), it.key());
336        connect(it.value(), SIGNAL(readyRead()),
337                _signalMapper, SLOT(map()));
338        ClientHasReadyRead(it.key());
339    }
340    _buffers.insert(MulticastNodeId, QList<Buffer>());
341
342    _isReady = true;
343    emit networkReady();
344    connect(qApp, SIGNAL(aboutToQuit()),
345            this, SLOT(TearDown()));
346}
347
348void Network::MulticastReady(QByteArray data){
349    delete _multicast;
350    _multicast = 0;
351
352    QByteArray plaintext, sig;
353    PrepareMessage(LogEntry::MULTICAST_FINAL, data, &plaintext, &sig);
354
355    foreach(const NodeInfo& node, _config->nodes)
356        if(node.node_id != _config->my_node_id && !node.excluded){
357            QTcpSocket* socket = _clients[node.node_id];
358            Q_ASSERT_X(socket, "Network::MulticastReady",
359                       (const char*) QString("socket[%1] = null")
360                       .arg(node.node_id).toUtf8().data());
361            Q_ASSERT_X(socket->state() == QAbstractSocket::ConnectedState,
362                       "Network::MulticastReady",
363                       (const char*) QString("socket[%1] wrong state")
364                       .arg(node.node_id).toUtf8().data());
365            int w_count = socket->write(plaintext);
366            w_count += socket->write(sig);
367            Q_ASSERT(w_count == plaintext.size() + sig.size());
368        }
369
370    LogEntry entry = { LogEntry::MULTICAST_FINAL, -1, _nonce, data, sig, true };
371    Buffer buffer;
372    buffer.status = Buffer::DONE;
373    buffer.entry = entry;
374    _buffers[MulticastNodeId].push_back(buffer);
375    if(_isReady)
376        emit readyRead(MulticastNodeId);
377}
378
379void Network::MulticastError(int node_id, const QString& reason){
380    emit inputError(node_id, reason);
381}
382
383void Network::TearDown(){
384    WaitForBytesWritten();
385    for(QMap<int, QTcpSocket*>::const_iterator it = _clients.constBegin();
386        it != _clients.constEnd(); ++it)
387        it.value()->close();
388}
389
390void Network::StartIncomingNetwork(){
391    if(_inReceivingPhase)
392        return;
393    // fprintf(stderr, "starting incoming network\n");
394
395    _inReceivingPhase = true;
396    for(QMap<int, QList<Buffer> >::const_iterator it = _buffers.constBegin();
397        it != _buffers.constEnd(); ++it){
398        // fprintf(stderr, "node %d: size = %d\n", it.key(), it.value().size());
399        if(it.value().size() > 0 && it.value().front().status == Buffer::DONE){
400            // fprintf(stderr, "node %d readyRead\n", it.key());
401            emit readyRead(it.key());
402        }
403    }
404}
405
406void Network::StopIncomingNetwork(){
407    _inReceivingPhase = false;
408}
409
410const char* const NetworkPrepare::ChallengePropertyName =
411    "NetworkPrepareChallenge";
412const char* const NetworkPrepare::NodeIdPropertyName =
413    "NetworkPrepareNodeId";
414const char* const NetworkPrepare::AnswerLengthPropertyName =
415    "NetworkPrepareAnswerLength";
416const int NetworkPrepare::ChallengeLength = 64;  // SHA-1 uses 512-bit blocks
417
418NetworkPrepare::NetworkPrepare(Configuration* config,
419                               QTcpServer* server,
420                               QMap<int, QTcpSocket*>* sockets)
421    : _config(config), _server(server), _sockets(sockets) {}
422
423void NetworkPrepare::DoPrepare(const QHostAddress& address, quint16 port){
424    connect(_server, SIGNAL(newConnection()),
425            this, SLOT(NewConnection()));
426    bool r = _server->listen(address, port);
427    // fprintf(stderr, "%s:%d: %s\n",
428    //        address.toString().toUtf8().data(), port,
429    //        r ? "true" : "false");
430    Q_ASSERT_X(r, "Network::Network(Configuration*)",
431               _server->errorString().toUtf8().data());
432
433    _incomeSignalMapper = new QSignalMapper(this);
434    connect(_incomeSignalMapper, SIGNAL(mapped(QObject*)),
435            this, SLOT(ReadNodeId(QObject*)));
436    _answerSignalMapper = new QSignalMapper(this);
437    connect(_answerSignalMapper, SIGNAL(mapped(QObject*)),
438            this, SLOT(ReadChallengeAnswer(QObject*)));
439
440    _connectSignalMapper = new QSignalMapper(this);
441    _errorSignalMapper = new QSignalMapper(this);
442    _challengeSignalMapper = new QSignalMapper(this);
443    connect(_challengeSignalMapper, SIGNAL(mapped(QObject*)),
444            this, SLOT(ReadChallenge(QObject*)));
445
446    QTimer::singleShot(1000, this, SLOT(TryConnect()));
447}
448
449void NetworkPrepare::AddSocket(int node_id, QTcpSocket* socket){
450    _sockets->insert(node_id, socket);
451    if(_sockets->size() < _config->num_nodes - 1)
452        return;
453
454    for(QMap<int, NodeInfo>::const_iterator it = _config->nodes.constBegin();
455        it != _config->nodes.constEnd(); ++it){
456        if(it.key() == _config->my_node_id)
457            continue;
458        QMap<int, QTcpSocket*>::const_iterator jt =
459            _sockets->constFind(it.key());
460        if(jt == _sockets->constEnd())
461            return;
462        QTcpSocket* s = *jt;
463        if(!s->isValid() || s->state() != QAbstractSocket::ConnectedState)
464            return;
465    }
466
467    disconnect(_server, SIGNAL(newConnection()),
468               this, SLOT(NewConnection()));
469    emit networkReady();
470}
471
472void NetworkPrepare::NewConnection(){
473    QTcpSocket* socket = _server->nextPendingConnection();
474
475    char challenge[ChallengeLength];
476    Random::GetInstance()->GetBlock(sizeof(challenge), challenge);
477    QByteArray ba(challenge, sizeof(challenge));
478    socket->setProperty(ChallengePropertyName, ba);
479
480    _incomeSignalMapper->setMapping(socket, socket);
481    connect(socket, SIGNAL(readyRead()), _incomeSignalMapper, SLOT(map()));
482
483    socket->write(ba);
484}
485
486void NetworkPrepare::ReadNodeId(QObject* o){
487    QTcpSocket* socket = qobject_cast<QTcpSocket*>(o);
488    Q_ASSERT(socket);
489
490    if(socket->bytesAvailable() < QByteArrayUtil::IntegerSize * 2)
491        return;
492
493    QByteArray data = socket->read(8);
494    int node_id = QByteArrayUtil::ExtractInt(true, &data);
495    int answer_length = QByteArrayUtil::ExtractInt(true, &data);
496
497    QHostInfo remote = QHostInfo::fromName(_config->nodes[node_id].addr);
498    if(!remote.addresses().contains(socket->peerAddress())){
499        // XXX(scw): wrong host message
500        fprintf(stderr, "peer %d expect from %s but from %s\n",
501               node_id,
502               (char*) _config->nodes[node_id].addr.toUtf8().data(),
503               (char*) socket->peerAddress().toString().toUtf8().data());
504        socket->disconnectFromHost();
505        delete socket;
506        return;
507    }
508
509    socket->setProperty(NodeIdPropertyName, node_id);
510    socket->setProperty(AnswerLengthPropertyName, answer_length);
511    _answerSignalMapper->setMapping(socket, socket);
512
513    disconnect(socket, SIGNAL(readyRead()), _incomeSignalMapper, SLOT(map()));
514    connect(socket, SIGNAL(readyRead()), _answerSignalMapper, SLOT(map()));
515    ReadChallengeAnswer(o);
516}
517
518void NetworkPrepare::ReadChallengeAnswer(QObject* o){
519    QTcpSocket* socket = qobject_cast<QTcpSocket*>(o);
520    Q_ASSERT(socket);
521
522    bool ok = false;
523    int answer_length =
524        socket->property(AnswerLengthPropertyName).toInt(&ok);
525    Q_ASSERT_X(ok, "NetworkPrepare::ReadChallengeAnswer",
526                   "anser length property not an integer");
527
528    if(socket->bytesAvailable() < answer_length)
529        return;
530
531    int node_id = socket->property(NodeIdPropertyName).toInt(&ok);
532    Q_ASSERT_X(ok, "NetworkPrepare::ReadChallengeAnswer",
533                   "node id property not an integer");
534
535    QByteArray challenge =
536        socket->property(ChallengePropertyName).toByteArray();
537    Q_ASSERT(challenge.size() == ChallengeLength);
538
539    QByteArray answer = socket->read(answer_length);
540    if(!Crypto::GetInstance()->Verify(
541                &_config->nodes[node_id].identity_pk,
542                challenge,
543                answer)){
544        // XXX(scw): challenge failed message
545        fprintf(stderr, "node %d challenge failed\n", node_id);
546        socket->disconnectFromHost();
547        delete socket;
548        return;
549    }
550
551    socket->setProperty(NodeIdPropertyName, QVariant());
552    socket->setProperty(AnswerLengthPropertyName, QVariant());
553    socket->setProperty(ChallengePropertyName, QVariant());
554    disconnect(socket, SIGNAL(readyRead()),
555               _answerSignalMapper, SLOT(map()));
556
557    AddSocket(node_id, socket);
558}
559
560void NetworkPrepare::TryConnect(){
561    connect(_connectSignalMapper, SIGNAL(mapped(QObject*)),
562            this, SLOT(Connected(QObject*)));
563    connect(_errorSignalMapper, SIGNAL(mapped(QObject*)),
564            this, SLOT(ConnectError(QObject*)));
565
566    foreach(const NodeInfo& node, _config->nodes){
567        if(node.node_id >= _config->my_node_id)
568            continue;
569
570        QTcpSocket* socket = new QTcpSocket(_server);
571        connect(socket, SIGNAL(connected()),
572                _connectSignalMapper, SLOT(map()));
573        connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
574                _errorSignalMapper, SLOT(map()));
575        _connectSignalMapper->setMapping(socket, socket);
576        _errorSignalMapper->setMapping(socket, socket);
577        socket->setProperty(NodeIdPropertyName, node.node_id);
578        socket->connectToHost(node.addr, node.port);
579    }
580}
581
582void NetworkPrepare::Connected(QObject* o){
583    QTcpSocket* socket = qobject_cast<QTcpSocket*>(o);
584    Q_ASSERT(socket);
585
586    disconnect(socket, SIGNAL(connected()),
587               _connectSignalMapper, SLOT(map()));
588    disconnect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
589               _errorSignalMapper, SLOT(map()));
590
591    _challengeSignalMapper->setMapping(socket, socket);
592    connect(socket, SIGNAL(readyRead()),
593            _challengeSignalMapper, SLOT(map()));
594    ReadChallenge(o);
595}
596
597void NetworkPrepare::ConnectError(QObject* o){
598    // XXX(scw): error message? retry count? wait before retry?
599    QTcpSocket* socket = qobject_cast<QTcpSocket*>(o);
600    Q_ASSERT(socket);
601
602    bool ok = false;
603    int node_id = socket->property(NodeIdPropertyName).toInt(&ok);
604    Q_ASSERT_X(ok, "NetworkPrepare::ConnectError",
605                   "node id property not an integer");
606
607    const NodeInfo& node = _config->nodes[node_id];
608    socket->connectToHost(node.addr, node.port);
609}
610
611void NetworkPrepare::ReadChallenge(QObject* o){
612    QTcpSocket* socket = qobject_cast<QTcpSocket*>(o);
613    Q_ASSERT(socket);
614
615    if(socket->bytesAvailable() < ChallengeLength)
616        return;
617
618    QByteArray challenge = socket->read(ChallengeLength);
619    QByteArray answer;
620    bool r = Crypto::GetInstance()->Sign(
621            &_config->identity_sk, challenge, &answer);
622    Q_ASSERT_X(r, "NetworkPrepare::ReadChallenge",
623                  "challeng signing failed");
624    QByteArrayUtil::PrependInt(answer.size(), &answer);
625    QByteArrayUtil::PrependInt(_config->my_node_id, &answer);
626    socket->write(answer);
627
628    bool ok = false;
629    int node_id = socket->property(NodeIdPropertyName).toInt(&ok);
630    Q_ASSERT_X(ok, "NetworkPrepare::ConnectError",
631                   "node id property not an integer");
632
633    socket->setProperty(NodeIdPropertyName, QVariant());
634    disconnect(socket, SIGNAL(readyRead()),
635               _challengeSignalMapper, SLOT(map()));
636
637    AddSocket(node_id, socket);
638}
639
640MulticastXorProcessor::MulticastXorProcessor(
641        Network* network, int num_nodes, const QByteArray& self_data)
642    : QObject(network), _numNodes(num_nodes), _data(self_data){}
643
644void MulticastXorProcessor::EnterMessage(
645        int node_id, const QByteArray& data){
646    if(_received.contains(node_id)){
647        emit multicastError(node_id, "Multiple message from the same node");
648        return;
649    }
650
651    char* p_d = _data.data();
652    const char* p_s = data.constData();
653    char* p_end = p_d + _data.size();
654    for(; p_d < p_end; ++p_d, ++p_s)
655        *p_d ^= *p_s;
656
657    _received.insert(node_id);
658    if(_received.size() == _numNodes - 1)
659        emit multicastReady(_data);
660}
661}
662// -*- vim:sw=4:expandtab:cindent: