blob: 69b43eb38956bccdd37ee208d2fcea74cb3abbf3 [file] [log] [blame]
Alexandre Lisione24852d2014-02-04 13:13:02 -05001/*
2 Copyright (C) 2006-2008 Werner Dittmann
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/*
19 * Authors: Werner Dittmann <Werner.Dittmann@t-online.de>
20 */
21// #define UNIT_TEST
22
23#include <string>
24#include <time.h>
25#include <stdlib.h>
26#include <unistd.h>
27
28#include <libzrtpcpp/ZIDFile.h>
29
30
31static ZIDFile* instance;
32static int errors = 0; // maybe we will use as member of ZIDFile later...
33
34void ZIDFile::createZIDFile(char* name) {
35 zidFile = fopen(name, "wb+");
36 // New file, generate an associated random ZID and save
37 // it as first record
38 if (zidFile != NULL) {
39 unsigned int* ip;
40 ip = (unsigned int*) associatedZid;
41 srand(time(NULL));
42 *ip++ = rand();
43 *ip++ = rand();
44 *ip = rand();
45
46 ZIDRecord rec(associatedZid);
47 rec.setOwnZIDRecord();
48 fseek(zidFile, 0L, SEEK_SET);
49 if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
50 ++errors;
51 fflush(zidFile);
52 }
53}
54
55/**
56 * Migrate old ZID file format to new one.
57 *
58 * If ZID file is old format:
59 * - close it, rename it, then re-open
60 * - create ZID file for new format
61 * - copy over contents and flags.
62 */
63void ZIDFile::checkDoMigration(char* name) {
64 FILE* fdOld;
65 unsigned char inb[2];
66 zidrecord1_t recOld;
67
68 fseek(zidFile, 0L, SEEK_SET);
69 if (fread(inb, 2, 1, zidFile) < 1) {
70 ++errors;
71 inb[0] = 0;
72 }
73
74 if (inb[0] > 0) { // if it's new format just return
75 return;
76 }
77 fclose(zidFile); // close old ZID file
78 zidFile = NULL;
79
80 // create save file name, rename and re-open
81 // if rename fails, just unlink old ZID file and create a brand new file
82 // just a little inconvenience for the user, need to verify new SAS
83 std::string fn = std::string(name) + std::string(".save");
84 if (rename(name, fn.c_str()) < 0) {
85 unlink(name);
86 createZIDFile(name);
87 return;
88 }
89 fdOld = fopen(fn.c_str(), "rb"); // reopen old format in read only mode
90
91 // Get first record from old file - is the own ZID
92 fseek(fdOld, 0L, SEEK_SET);
93 if (fread(&recOld, sizeof(zidrecord1_t), 1, fdOld) != 1) {
94 fclose(fdOld);
95 return;
96 }
97 if (recOld.ownZid != 1) {
98 fclose(fdOld);
99 return;
100 }
101 zidFile = fopen(name, "wb+"); // create new format file in binary r/w mode
102 if (zidFile == NULL) {
103 return;
104 }
105 // create ZIDRecord in new format, copy over own ZID and write the record
106 ZIDRecord rec(recOld.identifier);
107 rec.setOwnZIDRecord();
108 if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
109 ++errors;
110
111 // now copy over all valid records from old ZID file format.
112 // Sequentially read old records, sequentially write new records
113 int numRead;
114 do {
115 numRead = fread(&recOld, sizeof(zidrecord1_t), 1, fdOld);
116 if (numRead == 0) { // all old records processed
117 break;
118 }
119 // skip own ZID record and invalid records
120 if (recOld.ownZid == 1 || recOld.recValid == 0) {
121 continue;
122 }
123 ZIDRecord rec2(recOld.identifier);
124 rec2.setValid();
125 if (recOld.rs1Valid & SASVerified) {
126 rec2.setSasVerified();
127 }
128 rec2.setNewRs1(recOld.rs2Data);
129 rec2.setNewRs1(recOld.rs1Data);
130 if (fwrite(rec2.getRecordData(), rec2.getRecordLength(), 1, zidFile) < 1)
131 ++errors;
132
133 } while (numRead == 1);
134 fflush(zidFile);
135}
136
137ZIDFile::~ZIDFile() {
138 close();
139}
140
141ZIDFile* ZIDFile::getInstance() {
142
143 if (instance == NULL) {
144 instance = new ZIDFile();
145 }
146 return instance;
147}
148
149int ZIDFile::open(char* name) {
150
151 // check for an already active ZID file
152 if (zidFile != NULL) {
153 return 0;
154 }
155 if ((zidFile = fopen(name, "rb+")) == NULL) {
156 createZIDFile(name);
157 } else {
158 checkDoMigration(name);
159 if (zidFile != NULL) {
160 ZIDRecord rec;
161 fseek(zidFile, 0L, SEEK_SET);
162 if (fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) != 1) {
163 fclose(zidFile);
164 zidFile = NULL;
165 return -1;
166 }
167 if (!rec.isOwnZIDRecord()) {
168 fclose(zidFile);
169 zidFile = NULL;
170 return -1;
171 }
172 memcpy(associatedZid, rec.getIdentifier(), IDENTIFIER_LEN);
173 }
174 }
175 return ((zidFile == NULL) ? -1 : 1);
176}
177
178void ZIDFile::close() {
179
180 if (zidFile != NULL) {
181 fclose(zidFile);
182 zidFile = NULL;
183 }
184}
185
186unsigned int ZIDFile::getRecord(ZIDRecord* zidRecord) {
187 unsigned long pos;
188 ZIDRecord rec;
189 int numRead;
190
191 // set read pointer behind first record (
192 fseek(zidFile, rec.getRecordLength(), SEEK_SET);
193
194 do {
195 pos = ftell(zidFile);
196 numRead = fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile);
197 if (numRead == 0) {
198 break;
199 }
200
201 // skip own ZID record and invalid records
202 if (rec.isOwnZIDRecord() || !rec.isValid()) {
203 continue;
204 }
205
206 } while (numRead == 1 &&
207 memcmp(zidRecord->getIdentifier(), rec.getIdentifier(), IDENTIFIER_LEN) != 0);
208
209 // If we reached end of file, then no record with the ZID
210 // found. We need to create a new ZID record.
211 if (numRead == 0) {
212 // create new record
213 ZIDRecord rec1(zidRecord->getIdentifier());
214 rec1.setValid();
215 if (fwrite(rec1.getRecordData(), rec1.getRecordLength(), 1, zidFile) < 1)
216 ++errors;
217 memcpy(zidRecord->getRecordData(), rec1.getRecordData(), rec1.getRecordLength());
218 } else {
219 // Copy the read data into caller's the record storage
220 memcpy(zidRecord->getRecordData(), rec.getRecordData(), rec.getRecordLength());
221 }
222
223 // remember position of record in file for save operation
224 zidRecord->setPosition(pos);
225 return 1;
226}
227
228unsigned int ZIDFile::saveRecord(ZIDRecord *zidRecord) {
229
230 fseek(zidFile, zidRecord->getPosition(), SEEK_SET);
231 if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1)
232 ++errors;
233 fflush(zidFile);
234 return 1;
235}
236
237
238#ifdef UNIT_TEST
239
240#include <iostream>
241#include <unistd.h>
242using namespace std;
243
244static void hexdump(const char* title, const unsigned char *s, int l) {
245 int n=0;
246
247 if (s == NULL) return;
248
249 fprintf(stderr, "%s",title);
250 for (; n < l ; ++n) {
251 if ((n%16) == 0)
252 fprintf(stderr, "\n%04x",n);
253 fprintf(stderr, " %02x",s[n]);
254 }
255 fprintf(stderr, "\n");
256}
257
258int main(int argc, char *argv[]) {
259
260 unsigned char myId[IDENTIFIER_LEN];
261 ZIDFile *zid = ZIDFile::getInstance();
262
263 unlink("testzid2");
264 zid->open("testzid2");
265 hexdump("My ZID: ", zid->getZid(), IDENTIFIER_LEN);
266 memcpy(myId, zid->getZid(), IDENTIFIER_LEN);
267 zid->close();
268
269 zid->open("testzid2");
270 if (memcmp(myId, zid->getZid(), IDENTIFIER_LEN) != 0) {
271 cerr << "Wrong ZID in testfile" << endl;
272 return 1;
273 }
274
275 // Create a new ZID record for peer ZID "123456789012"
276 ZIDRecord zr3((unsigned char*) "123456789012");
277 zid->getRecord(&zr3);
278 if (!zr3.isValid()) {
279 cerr << "New ZID record '123456789012' not set to valid" << endl;
280 return 1;
281 }
282 zid->saveRecord(&zr3);
283
284 // create a second record with peer ZID "210987654321"
285 ZIDRecord zr4((unsigned char*) "210987654321");
286 zid->getRecord(&zr4);
287 if (!zr4.isValid()) {
288 cerr << "New ZID record '210987654321' not set to valid" << endl;
289 return 1;
290 }
291 zid->saveRecord(&zr4);
292
293 // now set a first RS1 with default expiration interval, check
294 // if set correctly, valid flag and expiration interval
295 zr3.setNewRs1((unsigned char*) "11122233344455566677788899900012");
296 if (memcmp(zr3.getRs1(), "11122233344455566677788899900012", RS_LENGTH) != 0) {
297 cerr << "RS1 was not set (111...012)" << endl;
298 return 1;
299 }
300 if (!zr3.isRs1Valid()) {
301 cerr << "RS1 was not set to valid state (111...012)" << endl;
302 return 1;
303 }
304 if (!zr3.isRs1NotExpired()) {
305 cerr << "RS1 expired (111...012)" << endl;
306 return 1;
307 }
308 if (zr3.isRs2Valid()) {
309 cerr << "RS2 was set to valid state (111...012)" << endl;
310 return 1;
311 }
312 zid->saveRecord(&zr3);
313
314 // create a second RS1, RS2 will become the first RS1, check
315 // if set correctly, valid flag and expiration interval for both
316 // RS1 and RS2
317 zr3.setNewRs1((unsigned char*) "00099988877766655544433322211121");
318 if (memcmp(zr3.getRs1(), "00099988877766655544433322211121", RS_LENGTH) != 0) {
319 cerr << "RS1 was not set (000...121)" << endl;
320 return 1;
321 }
322 if (!zr3.isRs1Valid()) {
323 cerr << "RS1 was not set to valid state (000...121)" << endl;
324 return 1;
325 }
326 if (!zr3.isRs1NotExpired()) {
327 cerr << "RS1 expired (000...121)" << endl;
328 return 1;
329 }
330 if (memcmp(zr3.getRs2(), "11122233344455566677788899900012", RS_LENGTH) != 0) {
331 cerr << "RS2 was not set (111...012)" << endl;
332 return 1;
333 }
334 if (!zr3.isRs2Valid()) {
335 cerr << "RS2 was not set to valid state (111...012)" << endl;
336 return 1;
337 }
338 if (!zr3.isRs2NotExpired()) {
339 cerr << "RS2 expired (111...012)" << endl;
340 return 1;
341 }
342 zid->saveRecord(&zr3);
343
344 zid->close();
345
346 // Reopen, check if first record is still valid, RSx vaild and
347 // not expired. Then manipulate 2nd record.
348 zid->open("testzid2");
349
350 ZIDRecord zr3a((unsigned char*) "123456789012");
351 zid->getRecord(&zr3a);
352 if (!zr3a.isValid()) {
353 cerr << "Re-read ZID record '123456789012' not set to valid" << endl;
354 return 1;
355 }
356 if (memcmp(zr3a.getRs1(), "00099988877766655544433322211121", RS_LENGTH) != 0) {
357 cerr << "re-read RS1 was not set (000...121)" << endl;
358 return 1;
359 }
360 if (!zr3a.isRs1Valid()) {
361 cerr << "Re-read RS1 was not set to valid state (000...121)" << endl;
362 return 1;
363 }
364 if (!zr3a.isRs1NotExpired()) {
365 cerr << "re-read RS1 expired (000...121)" << endl;
366 return 1;
367 }
368 if (memcmp(zr3a.getRs2(), "11122233344455566677788899900012", RS_LENGTH) != 0) {
369 cerr << "re-read RS2 was not set (111...012)" << endl;
370 return 1;
371 }
372 if (!zr3a.isRs2Valid()) {
373 cerr << "Re-read RS2 was not set to valid state (111...012)" << endl;
374 return 1;
375 }
376 if (!zr3a.isRs2NotExpired()) {
377 cerr << "Re-read RS2 expired (111...012)" << endl;
378 return 1;
379 }
380
381 ZIDRecord zr5((unsigned char*) "210987654321");
382 zid->getRecord(&zr5);
383
384
385 // set new RS1 with expire interval of 5 second, then check immediatly
386 zr5.setNewRs1((unsigned char*) "aaa22233344455566677788899900012", 5);
387 if (!zr5.isValid()) {
388 cerr << "Re-read ZID record '210987654321' not set to valid" << endl;
389 return 1;
390 }
391 if (memcmp(zr5.getRs1(), "aaa22233344455566677788899900012", RS_LENGTH) != 0) {
392 cerr << "RS1 (2) was not set (aaa...012)" << endl;
393 return 1;
394 }
395 if (!zr5.isRs1Valid()) {
396 cerr << "RS1 (2) was not set to valid state (aaa...012)" << endl;
397 return 1;
398 }
399 if (!zr5.isRs1NotExpired()) {
400 cerr << "RS1 (2) expired (aaa...012)" << endl;
401 return 1;
402 }
403
404 // wait for 6 second, now the expire check shall fail
405 sleep(6);
406 if (zr5.isRs1NotExpired()) {
407 cerr << "RS1 (2) is not expired after defined interval (aaa...012)" << endl;
408 return 1;
409 }
410
411 zr5.setNewRs1((unsigned char*) "bbb99988877766655544433322211121", 256);
412 zid->saveRecord(&zr5);
413
414 zid->close();
415
416 // Test migration
417 zid->open("testzidOld");
418 zid->close();
419
420}
421
422#endif
423
424/** EMACS **
425 * Local variables:
426 * mode: c++
427 * c-default-style: ellemtel
428 * c-basic-offset: 4
429 * End:
430 */