1
- // Copyright (c) 2011-2013 The Bitcoin developers
2
- // Distributed under the MIT/X11 software license, see the accompanying
1
+ // Copyright (c) 2011-2014 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
4
5
5
#include " paymentserver.h"
6
6
7
7
#include " bitcoinunits.h"
8
- #include " guiconstants.h"
9
8
#include " guiutil.h"
10
9
#include " optionsmodel.h"
11
10
19
18
20
19
#include < openssl/x509.h>
21
20
#include < openssl/x509_vfy.h>
21
+
22
22
#include < QApplication>
23
23
#include < QByteArray>
24
24
#include < QDataStream>
46
46
#include < QUrlQuery>
47
47
#endif
48
48
49
- using namespace std ;
50
49
using namespace boost ;
50
+ using namespace std ;
51
51
52
52
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000 ; // milliseconds
53
53
const QString BITCOIN_IPC_PREFIX (" bitcoin:" );
54
- const char * BITCOIN_REQUEST_MIMETYPE = " application/bitcoin-paymentrequest" ;
55
- const char * BITCOIN_PAYMENTACK_MIMETYPE = " application/bitcoin-paymentack" ;
56
- const char * BITCOIN_PAYMENTACK_CONTENTTYPE = " application/bitcoin-payment" ;
54
+ // BIP70 payment protocol messages
55
+ const char * BIP70_MESSAGE_PAYMENTACK = " PaymentACK" ;
56
+ const char * BIP70_MESSAGE_PAYMENTREQUEST = " PaymentRequest" ;
57
+ // BIP71 payment protocol media types
58
+ const char * BIP71_MIMETYPE_PAYMENT = " application/bitcoin-payment" ;
59
+ const char * BIP71_MIMETYPE_PAYMENTACK = " application/bitcoin-paymentack" ;
60
+ const char * BIP71_MIMETYPE_PAYMENTREQUEST = " application/bitcoin-paymentrequest" ;
61
+ // BIP70 max payment request size in bytes (DoS protection)
62
+ const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000 ;
57
63
58
64
X509_STORE* PaymentServer::certStore = NULL ;
59
65
void PaymentServer::freeCertStore ()
@@ -184,14 +190,18 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
184
190
// Warning: ipcSendCommandLine() is called early in init,
185
191
// so don't use "emit message()", but "QMessageBox::"!
186
192
//
187
- bool PaymentServer::ipcParseCommandLine (int argc, char * argv[])
193
+ void PaymentServer::ipcParseCommandLine (int argc, char * argv[])
188
194
{
189
195
for (int i = 1 ; i < argc; i++)
190
196
{
191
197
QString arg (argv[i]);
192
198
if (arg.startsWith (" -" ))
193
199
continue ;
194
200
201
+ // If the bitcoin: URI contains a payment request, we are not able to detect the
202
+ // network as that would require fetching and parsing the payment request.
203
+ // That means clicking such an URI which contains a testnet payment request
204
+ // will start a mainnet instance and throw a "wrong network" error.
195
205
if (arg.startsWith (BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
196
206
{
197
207
savedPaymentRequests.append (arg);
@@ -216,7 +226,7 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
216
226
savedPaymentRequests.append (arg);
217
227
218
228
PaymentRequestPlus request;
219
- if (readPaymentRequest (arg, request))
229
+ if (readPaymentRequestFromFile (arg, request))
220
230
{
221
231
if (request.getDetails ().network () == " main" )
222
232
{
@@ -235,7 +245,6 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[])
235
245
qWarning () << " PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg;
236
246
}
237
247
}
238
- return true ;
239
248
}
240
249
241
250
//
@@ -254,6 +263,7 @@ bool PaymentServer::ipcSendCommandLine()
254
263
if (!socket->waitForConnected (BITCOIN_IPC_CONNECT_TIMEOUT))
255
264
{
256
265
delete socket;
266
+ socket = NULL ;
257
267
return false ;
258
268
}
259
269
@@ -262,12 +272,14 @@ bool PaymentServer::ipcSendCommandLine()
262
272
out.setVersion (QDataStream::Qt_4_0);
263
273
out << r;
264
274
out.device ()->seek (0 );
275
+
265
276
socket->write (block);
266
277
socket->flush ();
267
-
268
278
socket->waitForBytesWritten (BITCOIN_IPC_CONNECT_TIMEOUT);
269
279
socket->disconnectFromServer ();
280
+
270
281
delete socket;
282
+ socket = NULL ;
271
283
fResult = true ;
272
284
}
273
285
@@ -440,7 +452,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
440
452
{
441
453
PaymentRequestPlus request;
442
454
SendCoinsRecipient recipient;
443
- if (!readPaymentRequest (s, request))
455
+ if (!readPaymentRequestFromFile (s, request))
444
456
{
445
457
emit message (tr (" Payment request file handling" ),
446
458
tr (" Payment request file cannot be read! This can be caused by an invalid payment request file." ),
@@ -474,18 +486,25 @@ void PaymentServer::handleURIConnection()
474
486
handleURIOrFile (msg);
475
487
}
476
488
477
- bool PaymentServer::readPaymentRequest (const QString& filename, PaymentRequestPlus& request)
489
+ //
490
+ // Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
491
+ // so don't use "emit message()", but "QMessageBox::"!
492
+ //
493
+ bool PaymentServer::readPaymentRequestFromFile (const QString& filename, PaymentRequestPlus& request)
478
494
{
479
495
QFile f (filename);
480
- if (!f.open (QIODevice::ReadOnly))
481
- {
482
- qWarning () << " PaymentServer::readPaymentRequest : Failed to open " << filename;
496
+ if (!f.open (QIODevice::ReadOnly)) {
497
+ qWarning () << QString (" PaymentServer::%1: Failed to open %2" ).arg (__func__).arg (filename);
483
498
return false ;
484
499
}
485
500
486
- if (f.size () > MAX_PAYMENT_REQUEST_SIZE)
487
- {
488
- qWarning () << " PaymentServer::readPaymentRequest : " << filename << " too large" ;
501
+ // BIP70 DoS protection
502
+ if (f.size () > BIP70_MAX_PAYMENTREQUEST_SIZE) {
503
+ qWarning () << QString (" PaymentServer::%1: Payment request %2 is too large (%3 bytes, allowed %4 bytes)." )
504
+ .arg (__func__)
505
+ .arg (filename)
506
+ .arg (f.size ())
507
+ .arg (BIP70_MAX_PAYMENTREQUEST_SIZE);
489
508
return false ;
490
509
}
491
510
@@ -580,10 +599,10 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins
580
599
void PaymentServer::fetchRequest (const QUrl& url)
581
600
{
582
601
QNetworkRequest netRequest;
583
- netRequest.setAttribute (QNetworkRequest::User, " PaymentRequest " );
602
+ netRequest.setAttribute (QNetworkRequest::User, BIP70_MESSAGE_PAYMENTREQUEST );
584
603
netRequest.setUrl (url);
585
604
netRequest.setRawHeader (" User-Agent" , CLIENT_NAME.c_str ());
586
- netRequest.setRawHeader (" Accept" , BITCOIN_REQUEST_MIMETYPE );
605
+ netRequest.setRawHeader (" Accept" , BIP71_MIMETYPE_PAYMENTREQUEST );
587
606
netManager->get (netRequest);
588
607
}
589
608
@@ -594,11 +613,11 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
594
613
return ;
595
614
596
615
QNetworkRequest netRequest;
597
- netRequest.setAttribute (QNetworkRequest::User, " PaymentACK " );
616
+ netRequest.setAttribute (QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK );
598
617
netRequest.setUrl (QString::fromStdString (details.payment_url ()));
599
- netRequest.setHeader (QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE );
618
+ netRequest.setHeader (QNetworkRequest::ContentTypeHeader, BIP71_MIMETYPE_PAYMENT );
600
619
netRequest.setRawHeader (" User-Agent" , CLIENT_NAME.c_str ());
601
- netRequest.setRawHeader (" Accept" , BITCOIN_PAYMENTACK_MIMETYPE );
620
+ netRequest.setRawHeader (" Accept" , BIP71_MIMETYPE_PAYMENTACK );
602
621
603
622
payments::Payment payment;
604
623
payment.set_merchant_data (details.merchant_data ());
@@ -616,7 +635,6 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
616
635
else {
617
636
CPubKey newKey;
618
637
if (wallet->GetKeyFromPool (newKey)) {
619
- LOCK (wallet->cs_wallet ); // SetAddressBook
620
638
CKeyID keyID = newKey.GetID ();
621
639
wallet->SetAddressBook (keyID, strAccount, " refund" );
622
640
@@ -646,21 +664,34 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien
646
664
void PaymentServer::netRequestFinished (QNetworkReply* reply)
647
665
{
648
666
reply->deleteLater ();
649
- if (reply->error () != QNetworkReply::NoError)
650
- {
667
+
668
+ // BIP70 DoS protection
669
+ if (reply->size () > BIP70_MAX_PAYMENTREQUEST_SIZE) {
670
+ QString msg = tr (" Payment request %2 is too large (%3 bytes, allowed %4 bytes)." )
671
+ .arg (__func__)
672
+ .arg (reply->request ().url ().toString ())
673
+ .arg (reply->size ())
674
+ .arg (BIP70_MAX_PAYMENTREQUEST_SIZE);
675
+
676
+ qWarning () << QString (" PaymentServer::%1:" ).arg (__func__) << msg;
677
+ emit message (tr (" Payment request DoS protection" ), msg, CClientUIInterface::MSG_ERROR);
678
+ return ;
679
+ }
680
+
681
+ if (reply->error () != QNetworkReply::NoError) {
651
682
QString msg = tr (" Error communicating with %1: %2" )
652
683
.arg (reply->request ().url ().toString ())
653
684
.arg (reply->errorString ());
654
685
655
- qWarning () << " PaymentServer::netRequestFinished : " << msg;
686
+ qWarning () << " PaymentServer::netRequestFinished: " << msg;
656
687
emit message (tr (" Payment request error" ), msg, CClientUIInterface::MSG_ERROR);
657
688
return ;
658
689
}
659
690
660
691
QByteArray data = reply->readAll ();
661
692
662
693
QString requestType = reply->request ().attribute (QNetworkRequest::User).toString ();
663
- if (requestType == " PaymentRequest " )
694
+ if (requestType == BIP70_MESSAGE_PAYMENTREQUEST )
664
695
{
665
696
PaymentRequestPlus request;
666
697
SendCoinsRecipient recipient;
@@ -676,7 +707,7 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply)
676
707
677
708
return ;
678
709
}
679
- else if (requestType == " PaymentACK " )
710
+ else if (requestType == BIP70_MESSAGE_PAYMENTACK )
680
711
{
681
712
payments::PaymentACK paymentACK;
682
713
if (!paymentACK.ParseFromArray (data.data (), data.size ()))
0 commit comments