-
Notifications
You must be signed in to change notification settings - Fork 220
/
Copy pathSafeFile.cpp
598 lines (467 loc) · 13.8 KB
/
SafeFile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
//
// This file is part of the aMule Project.
//
// Copyright (c) 2003-2011 aMule Team ( [email protected] / http://www.amule.org )
// Copyright (c) 2002-2011 Merkur ( [email protected] / http://www.emule-project.net )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
//
#include "SafeFile.h" // Interface declarations.
#include "MD4Hash.h" // Needed for CMD4Hash
#include "kademlia/utils/UInt128.h" // Needed for CUInt128
#include "ScopedPtr.h" // Needed for CScopedPtr and CScopedArray
#include "Logger.h"
#include <common/Format.h> // Needed for CFormat
#include "CompilerSpecific.h" // Needed for __FUNCTION__
#define CHECK_BOM(size, x) ((size >= 3) && (x[0] == (char)0xEF) && (x[1] == (char)0xBB) && (x[2] == (char)0xBF))
const char BOMHeader[3] = { '\xEF', '\xBB', '\xBF'};
CSafeIOException::CSafeIOException(const wxString& type, const wxString& desc)
: CMuleException(wxT("SafeIO::") + type, desc) {}
CEOFException::CEOFException(const wxString& desc)
: CSafeIOException(wxT("EOF"), desc) {}
CIOFailureException::CIOFailureException(const wxString& desc)
: CSafeIOException(wxT("IOFailure"), desc) {}
CIOFailureException::CIOFailureException(const wxString& type, const wxString& desc)
: CSafeIOException(wxT("IOFailure::") + type, desc) {}
///////////////////////////////////////////////////////////////////////////////
// CFileDataIO
CFileDataIO::~CFileDataIO()
{
}
bool CFileDataIO::Eof() const
{
return GetPosition() >= GetLength();
}
void CFileDataIO::Read(void *buffer, size_t count) const
{
MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to write to NULL buffer."));
// Check that we read everything we wanted.
if (doRead(buffer, count) == (signed)count) {
return;
}
// To reduce potential system calls, we only do EOF checks when reads fail.
if (Eof()) {
throw CEOFException(wxT("Attempt to read past end of file."));
} else {
throw CIOFailureException(wxT("Read error, failed to read from file."));
}
}
void CFileDataIO::Write(const void* buffer, size_t count)
{
MULE_VALIDATE_PARAMS(buffer, wxT("Attempting to read from NULL buffer."));
if (doWrite(buffer, count) != (signed)count) {
throw CIOFailureException(wxT("Write error, failed to write to file."));
}
}
uint64 CFileDataIO::Seek(sint64 offset, wxSeekMode from) const
{
sint64 newpos = 0;
switch (from) {
case wxFromStart:
newpos = offset;
break;
case wxFromCurrent:
newpos = GetPosition() + offset;
break;
case wxFromEnd:
newpos = GetLength() + offset;
break;
default:
MULE_VALIDATE_PARAMS(false, wxT("Invalid seek-mode specified."));
}
MULE_VALIDATE_PARAMS(newpos >= 0, wxT("Position after seeking would be less than zero!"));
sint64 result = doSeek(newpos);
MULE_VALIDATE_STATE(result >= 0, wxT("Seeking resulted in invalid offset."));
MULE_VALIDATE_STATE(result == newpos, wxT("Target position and actual position disagree."));
return result;
}
uint8 CFileDataIO::ReadUInt8() const
{
uint8 value = 0;
Read(&value, sizeof(uint8));
return value;
}
uint16 CFileDataIO::ReadUInt16() const
{
uint16 value = 0;
Read(&value, sizeof(uint16));
return ENDIAN_SWAP_16(value);
}
uint32 CFileDataIO::ReadUInt32() const
{
uint32 value = 0;
Read(&value, sizeof(uint32));
return ENDIAN_SWAP_32(value);
}
uint64 CFileDataIO::ReadUInt64() const
{
uint64 value = 0;
Read(&value, sizeof(uint64));
return ENDIAN_SWAP_64(value);
}
// UInt128 values are stored a little weird way...
// Four little-endian 32-bit numbers, stored in
// big-endian order
CUInt128 CFileDataIO::ReadUInt128() const
{
CUInt128 value;
for (int i = 0; i < 4; i++) {
// Four 32bits chunks
value.Set32BitChunk(i, ReadUInt32());
}
return value;
}
CMD4Hash CFileDataIO::ReadHash() const
{
CMD4Hash value;
Read(value.GetHash(), MD4HASH_LENGTH);
return value;
}
float CFileDataIO::ReadFloat() const
{
float retVal;
Read(&retVal, sizeof(float));
return retVal;
}
unsigned char* CFileDataIO::ReadBsob(uint8* puSize) const
{
MULE_VALIDATE_PARAMS(puSize, wxT("NULL pointer argument in ReadBsob"));
*puSize = ReadUInt8();
CScopedArray<unsigned char> bsob(*puSize);
Read(bsob.get(), *puSize);
return bsob.release();
}
wxString CFileDataIO::ReadString(bool bOptUTF8, uint8 SizeLen, bool SafeRead) const
{
uint32 readLen;
switch (SizeLen) {
case sizeof(uint16): readLen = ReadUInt16(); break;
case sizeof(uint32): readLen = ReadUInt32(); break;
default:
MULE_VALIDATE_PARAMS(false, wxT("Invalid SizeLen value in ReadString"));
}
if (SafeRead) {
readLen = std::min<uint64>(readLen, GetLength() - GetPosition());
}
return ReadOnlyString(bOptUTF8, readLen);
}
wxString CFileDataIO::ReadOnlyString(bool bOptUTF8, uint16 raw_len) const
{
// We only need to set the the NULL terminator, since we know that
// reads will either succeed or throw an exception, in which case
// we wont be returning anything
std::vector<char> val_array(raw_len + 1);
val_array[raw_len] = 0;
char* val = &(val_array[0]);
Read(val, raw_len);
wxString str;
if (CHECK_BOM(raw_len, val)) {
// This is a UTF8 string with a BOM header, skip header.
str = UTF82unicode(val + 3);
} else if (bOptUTF8) {
str = UTF82unicode(val);
if (str.IsEmpty()) {
// Fallback to Latin-1
str = wxString(val, wxConvISO8859_1, raw_len);
}
} else {
// Raw strings are written as Latin-1 (see CFileDataIO::WriteStringCore)
str = wxString(val, wxConvISO8859_1, raw_len);
}
return str;
}
void CFileDataIO::WriteUInt8(uint8 value)
{
Write(&value, sizeof(uint8));
}
void CFileDataIO::WriteUInt16(uint16 value)
{
ENDIAN_SWAP_I_16(value);
Write(&value, sizeof(uint16));
}
void CFileDataIO::WriteUInt32(uint32 value)
{
ENDIAN_SWAP_I_32(value);
Write(&value, sizeof(uint32));
}
void CFileDataIO::WriteUInt64(uint64 value)
{
ENDIAN_SWAP_I_64(value);
Write(&value, sizeof(uint64));
}
// UInt128 values are stored a little weird way...
// Four little-endian 32-bit numbers, stored in
// big-endian order
void CFileDataIO::WriteUInt128(const Kademlia::CUInt128& value)
{
for (int i = 0; i < 4; i++) {
// Four 32bits chunks
WriteUInt32(value.Get32BitChunk(i));
}
}
void CFileDataIO::WriteHash(const CMD4Hash& value)
{
Write(value.GetHash(), MD4HASH_LENGTH);
}
void CFileDataIO::WriteFloat(float value)
{
Write(&value, sizeof(float));
}
void CFileDataIO::WriteBsob(const unsigned char* value, uint8 size)
{
WriteUInt8(size);
Write(value, size);
}
void CFileDataIO::WriteString(const wxString& str, EUtf8Str eEncode, uint8 SizeLen)
{
switch (eEncode) {
case utf8strRaw:
case utf8strOptBOM: {
Unicode2CharBuf s(unicode2UTF8(str));
if (s.data()) {
WriteStringCore(s, eEncode, SizeLen);
break;
}
}
/* fall through */
default: {
// Non UTF-8 strings are saved as Latin-1
wxCharBuffer s1 = str.mb_str(wxConvISO8859_1);
WriteStringCore(s1, utf8strNone, SizeLen);
}
}
}
void CFileDataIO::WriteStringCore(const char *s, EUtf8Str eEncode, uint8 SizeLen)
{
uint32 sLength = s ? strlen(s) : 0;
uint32 real_length = 0;
if (eEncode == utf8strOptBOM) {
real_length = sLength + 3; // For BOM header.
} else {
real_length = sLength;
}
switch (SizeLen) {
case 0:
// Don't write size.
break;
case sizeof(uint16):
// We must not allow too long strings to be written,
// as this would allow for a buggy clients to "poison"
// us, by sending ISO8859-1 strings that expand to a
// greater than 16b length when converted as UTF-8.
if (real_length > 0xFFFF) {
wxFAIL_MSG(wxT("String is too long to be saved"));
real_length = std::min<uint32>(real_length, 0xFFFF);
if (eEncode == utf8strOptBOM) {
sLength = real_length - 3;
} else {
sLength = real_length;
}
}
WriteUInt16(real_length);
break;
case sizeof(uint32):
WriteUInt32(real_length);
break;
default:
MULE_VALIDATE_PARAMS(false, wxT("Invalid length for string-length field."));
}
// The BOM header must be written even if the string is empty.
if (eEncode == utf8strOptBOM) {
Write(BOMHeader, 3);
}
// Only attempt to write non-NULL strings.
if (sLength) {
// No NULL terminator is written since we explicitly specify the length
Write(s, sLength);
}
}
CTag *CFileDataIO::ReadTag(bool bOptACP) const
{
CTag *retVal = NULL;
wxString name;
uint8_t type = 0;
try {
type = ReadUInt8();
name = ReadString(false);
switch (type)
{
// NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
// the net in some months.
//
// And still.. it doesn't work this way without breaking backward compatibility. To properly
// do this without messing up the network the following would have to be done:
// - those tag types have to be ignored by any client, otherwise those tags would also be sent (and
// that's really the problem)
//
// - ignoring means, each client has to read and right throw away those tags, so those tags get
// get never stored in any tag list which might be sent by that client to some other client.
//
// - all calling functions have to be changed to deal with the 'nr. of tags' attribute (which was
// already parsed) correctly.. just ignoring those tags here is not enough, any taglists have to
// be built with the knowledge that the 'nr. of tags' attribute may get decreased during the tag
// reading..
//
// If those new tags would just be stored and sent to remote clients, any malicious or just bugged
// client could let send a lot of nodes "corrupted" packets...
//
case TAGTYPE_HASH16:
{
retVal = new CTagHash(name, ReadHash());
break;
}
case TAGTYPE_STRING:
retVal = new CTagString(name, ReadString(bOptACP));
break;
case TAGTYPE_UINT64:
retVal = new CTagInt64(name, ReadUInt64());
break;
case TAGTYPE_UINT32:
retVal = new CTagInt32(name, ReadUInt32());
break;
case TAGTYPE_UINT16:
retVal = new CTagInt16(name, ReadUInt16());
break;
case TAGTYPE_UINT8:
retVal = new CTagInt8(name, ReadUInt8());
break;
case TAGTYPE_FLOAT32:
retVal = new CTagFloat(name, ReadFloat());
break;
// NOTE: This tag data type is accepted and stored only to give us the possibility to upgrade
// the net in some months.
//
// And still.. it doesn't work this way without breaking backward compatibility
case TAGTYPE_BSOB:
{
uint8 size = 0;
CScopedArray<unsigned char> value(ReadBsob(&size));
retVal = new CTagBsob(name, value.get(), size);
break;
}
default:
throw wxString(CFormat(wxT("Invalid Kad tag type; type=0x%02x name=%s\n")) % type % name);
}
} catch(const CMuleException& e) {
AddLogLineN(e.what());
delete retVal;
throw;
} catch(const wxString& e) {
AddLogLineN(e);
throw;
}
return retVal;
}
void CFileDataIO::ReadTagPtrList(TagPtrList* taglist, bool bOptACP) const
{
MULE_VALIDATE_PARAMS(taglist, wxT("NULL pointer argument in ReadTagPtrList"));
uint32 count = ReadUInt8();
for (uint32 i = 0; i < count; i++)
{
CTag* tag = ReadTag(bOptACP);
taglist->push_back(tag);
}
}
void CFileDataIO::WriteTag(const CTag& tag)
{
try
{
WriteUInt8(tag.GetType());
if (!tag.GetName().IsEmpty()) {
WriteString(tag.GetName(),utf8strNone);
} else {
WriteUInt16(1);
WriteUInt8(tag.GetNameID());
}
switch (tag.GetType())
{
case TAGTYPE_HASH16:
// Do NOT use this to transfer any tags for at least half a year!!
WriteHash(CMD4Hash(tag.GetHash()));
break;
case TAGTYPE_STRING:
WriteString(tag.GetStr(), utf8strRaw); // Always UTF8
break;
case TAGTYPE_UINT64:
WriteUInt64(tag.GetInt());
break;
case TAGTYPE_UINT32:
WriteUInt32(tag.GetInt());
break;
case TAGTYPE_FLOAT32:
WriteFloat(tag.GetFloat());
break;
case TAGTYPE_BSOB:
WriteBsob(tag.GetBsob(), tag.GetBsobSize());
break;
case TAGTYPE_UINT16:
WriteUInt16(tag.GetInt());
break;
case TAGTYPE_UINT8:
WriteUInt8(tag.GetInt());
break;
case TAGTYPE_BLOB:
// NOTE: This will break backward compatibility with met files for eMule versions prior to 0.44a
// and any aMule prior to SVN 26/02/2005
WriteUInt32(tag.GetBlobSize());
Write(tag.GetBlob(), tag.GetBlobSize());
break;
default:
//TODO: Support more tag types
// With the if above, this should NEVER happen.
AddLogLineNS(CFormat(wxT("CFileDataIO::WriteTag: Unknown tag: type=0x%02X")) % tag.GetType());
wxFAIL;
break;
}
} catch (...) {
AddLogLineNS(wxT("Exception in CDataIO:WriteTag"));
throw;
}
}
void CFileDataIO::WriteTagPtrList(const TagPtrList& tagList)
{
uint32 count = tagList.size();
wxASSERT( count <= 0xFF );
WriteUInt8(count);
TagPtrList::const_iterator it;
for (it = tagList.begin(); it != tagList.end(); ++it) {
WriteTag(**it);
}
}
uint64 CFileDataIO::GetIntTagValue() const {
uint8 type = ReadUInt8();
ReadString(false);
switch (type) {
case TAGTYPE_UINT64:
return ReadUInt64();
break;
case TAGTYPE_UINT32:
return ReadUInt32();
break;
case TAGTYPE_UINT16:
return ReadUInt16();
break;
case TAGTYPE_UINT8:
return ReadUInt8();
break;
default:
throw wxString(wxT("Wrong tag type reading int tag"));
}
}
// File_checked_for_headers