~ubuntu-branches/ubuntu/saucy/resiprocate/saucy-proposed

« back to all changes in this revision

Viewing changes to resip/stack/test/testTransactionFSM.cxx

  • Committer: Package Import Robot
  • Author(s): Daniel Pocock
  • Date: 2012-05-17 19:29:59 UTC
  • Revision ID: package-import@ubuntu.com-20120517192959-vv00m77isztdy64q
Tags: upstream-1.8.2
ImportĀ upstreamĀ versionĀ 1.8.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// How this works:
 
2
//
 
3
// The first argument to this program needs to be a file that has a test in it.
 
4
//
 
5
// Clauses in this file can be: inject_wire, expect_wire, expect_tu,
 
6
// inject_tu, and delay.  A keyword needs to be by itself on a line.  They
 
7
// need to be in this form
 
8
//
 
9
// inject_wire (or inject_tu)
 
10
// {
 
11
// ...put a SIP message here that has a CRLF after each line...
 
12
// }
 
13
//
 
14
// expect_wire (or expect_tu)
 
15
// {
 
16
// status=100 (or method=REGISTER)
 
17
// timeout=32
 
18
// }
 
19
//
 
20
// delay
 
21
// {
 
22
// timeout=48
 
23
// }
 
24
//
 
25
// The inject_* clauses inject the SIP message described at the "wire"
 
26
// or the "tu" level.  The expect_* clauses indicate that the test
 
27
// specification expects to read a certain response (distinguished only
 
28
// by its response code) or request (distinguished only by its method
 
29
// name).  The expect_* clauses don't block but will queue the
 
30
// expectation for the duration of the timeout specified.  When you
 
31
// really want to sit and wait for a message, insert a delay clause.
 
32
// This is used as a "barrier" at which you want all the above timeouts
 
33
// to happen.  You'll want one at the end of your test, for example.
 
34
 
 
35
#ifndef __MINGW32__
 
36
 
 
37
#include <sys/time.h>
 
38
#include <sys/types.h>
 
39
#include <sys/socket.h>
 
40
#include <sys/stat.h>
 
41
#include <netinet/in.h>
 
42
#include <arpa/inet.h>
 
43
#include <errno.h>
 
44
#include <fcntl.h>
 
45
#include <signal.h>
 
46
#include <stdlib.h>
 
47
#include <string.h>
 
48
#include <time.h>
 
49
#include <unistd.h>
 
50
 
 
51
#include <list>
 
52
#include <iostream>
 
53
#include <fstream>
 
54
#include <string>
 
55
 
 
56
#include "rutil/Data.hxx"
 
57
#include "rutil/DataStream.hxx"
 
58
#include "rutil/Logger.hxx"
 
59
#include "rutil/Socket.hxx"
 
60
 
 
61
#include "resip/stack/test/TestSupport.hxx"
 
62
#include "resip/stack/MethodTypes.hxx"
 
63
#include "resip/stack/SipStack.hxx"
 
64
#include "resip/stack/SipMessage.hxx"
 
65
#include "resip/stack/Symbols.hxx"
 
66
#include "resip/stack/Transport.hxx"
 
67
#include "rutil/ParseBuffer.hxx"
 
68
 
 
69
using namespace resip;
 
70
using namespace std;
 
71
 
 
72
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST
 
73
#define PORT 5060
 
74
 
 
75
 
 
76
// --------------------------------------------------
 
77
 
 
78
namespace resip 
 
79
{
 
80
class TestFSM  // this class is a friend of the SipStack and can directly access private stuff
 
81
{
 
82
   public:
 
83
      static void addMessage(SipStack* stack,SipMessage* message)
 
84
      {
 
85
         stack->mTransactionController->mStateMacFifo.add(message);
 
86
      }
 
87
};
 
88
}
 
89
 
 
90
typedef struct {
 
91
      struct timeval mExpiry;
 
92
      bool mIsRequest;
 
93
      bool mIsTransport;
 
94
      int mResponseCode;
 
95
      MethodTypes mMethod;
 
96
} WaitNode;
 
97
 
 
98
// --------------------------------------------------
 
99
 
 
100
char* ProgramName = 0;
 
101
char* TestSpecBuf = 0;
 
102
ParseBuffer* TestSpecParseBuf = 0;
 
103
list<WaitNode*> WaitQueue;
 
104
SipStack* client = 0;
 
105
FdSet clientFdSet;
 
106
struct sockaddr_in clientSa;
 
107
int clientFd;
 
108
Fifo<SipMessage> fakeTxFifo;
 
109
int errorCount = 0;
 
110
 
 
111
// --------------------------------------------------
 
112
 
 
113
// This is pure evil.  We interpose our own version of sendto
 
114
// so that this gets called by the UDP transport instead of the libc
 
115
// version.
 
116
int
 
117
sendto(int s, const void *msg, size_t len, int flags,
 
118
       const struct sockaddr *to, int tolen)
 
119
{
 
120
    fakeTxFifo.add(TestSupport::makeMessage(Data((const char *)msg, (int)len), true));
 
121
    return len;
 
122
}
 
123
 
 
124
// --------------------------------------------------
 
125
 
 
126
 
 
127
void
 
128
exitusage()
 
129
{
 
130
        cerr << "Usage: " << ProgramName << " ";
 
131
        cerr << "<testfileofsipmessagesandstuff>" << endl;
 
132
        exit(1);
 
133
}
 
134
 
 
135
extern "C" { void processTimeouts(int arg); }
 
136
 
 
137
void
 
138
processTimeouts(int arg)
 
139
{
 
140
    client->buildFdSet(clientFdSet);
 
141
    client->process(clientFdSet);
 
142
 
 
143
    if (WaitQueue.empty())
 
144
    {
 
145
        return;
 
146
    }
 
147
    SipMessage* message = 0;
 
148
 
 
149
 
 
150
#if defined(UGLY) || 1
 
151
    // This should:
 
152
    // 1. Take all messages from the "wire" and "tu" fifos
 
153
    // 2. For each message, look through the WaitQueue and see
 
154
    //    if the new message matches something we were waiting for.
 
155
    //    If yes, through away the queue entry, else raise a warning.
 
156
    // 3. When all messages from the "wire" and "tu" have been
 
157
    //    examined, see if anything in the queue has expired.
 
158
    //    If yes, warn, else just continue.
 
159
 
 
160
    // First go through the "wire" data
 
161
    while (fakeTxFifo.messageAvailable())
 
162
    {
 
163
        message = fakeTxFifo.getNext();
 
164
        for (list<WaitNode*>::iterator i = WaitQueue.begin();
 
165
             i != WaitQueue.end();
 
166
             /* don't increment */)
 
167
        {
 
168
            if ((*i)->mIsRequest && message->isRequest())
 
169
            {
 
170
                if ((*i)->mMethod == message->header(h_RequestLine).getMethod())
 
171
                {
 
172
                    // We matched something we expected.
 
173
                    delete message;
 
174
                    message = 0;
 
175
                    delete *i;
 
176
                    WaitQueue.erase(i++);
 
177
                    break;
 
178
                }
 
179
                else
 
180
                {
 
181
                    ++i;
 
182
                }
 
183
            }
 
184
            else if (!(*i)->mIsRequest && message->isResponse())
 
185
            {
 
186
                if ((*i)->mResponseCode ==
 
187
                    message->header(h_StatusLine).responseCode())
 
188
                {
 
189
                    // We matched something we expected.
 
190
                    delete message;
 
191
                    message = 0;
 
192
                    delete *i;
 
193
                    WaitQueue.erase(i++);
 
194
                    break;
 
195
                }
 
196
                else
 
197
                {
 
198
                    ++i;
 
199
                }
 
200
            }
 
201
            else
 
202
            {
 
203
                ++i;
 
204
            }
 
205
        }
 
206
        if (message)
 
207
        {
 
208
            DebugLog( << "Warning: unexpected message seen at the transport: " 
 
209
                      << message);
 
210
        }
 
211
        else
 
212
        {
 
213
            DebugLog( << "Success: expected message seen at the transport");
 
214
        }
 
215
        delete message;
 
216
    }
 
217
 
 
218
    // Now go through the data at the TU.
 
219
    while (0 != (message = client->receive()))
 
220
    {
 
221
        for (list<WaitNode*>::iterator i = WaitQueue.begin();
 
222
             i != WaitQueue.end();
 
223
             /* don't increment */)
 
224
        {
 
225
            if ((*i)->mIsRequest && message->isRequest())
 
226
            {
 
227
                if ((*i)->mMethod ==
 
228
                    message->header(h_RequestLine).getMethod())
 
229
                {
 
230
                    // We matched something we expected.
 
231
                    delete message;
 
232
                    message = 0;
 
233
                    delete *i;
 
234
                    WaitQueue.erase(i++);
 
235
                    break;
 
236
                }
 
237
                else
 
238
                {
 
239
                    ++i;
 
240
                }
 
241
            }
 
242
            else if (!(*i)->mIsRequest && message->isResponse())
 
243
            {
 
244
                if ((*i)->mResponseCode ==
 
245
                    message->header(h_StatusLine).responseCode())
 
246
                {
 
247
                    // We matched something we expected.
 
248
                    delete message;
 
249
                    message = 0;
 
250
                    delete *i;
 
251
                    WaitQueue.erase(i++);
 
252
                    break;
 
253
                }
 
254
                else
 
255
                {
 
256
                    ++i;
 
257
                }
 
258
            }
 
259
            else
 
260
            {
 
261
                ++i;
 
262
            }
 
263
        }
 
264
        if (message)
 
265
        {
 
266
            DebugLog( << "Warning: unexpected message seen at the TU: "
 
267
                      << *message);
 
268
            delete message;
 
269
        }
 
270
        else
 
271
        {
 
272
            DebugLog( << "Success: expected message seen at TU");
 
273
        }
 
274
    }
 
275
 
 
276
    // Print the list of expected events that have failed to happen withing
 
277
    // the specified timeout.
 
278
    for (list<WaitNode*>::iterator i = WaitQueue.begin();
 
279
         i != WaitQueue.end();
 
280
         /* don't increment */)
 
281
    {
 
282
        struct timeval tv;
 
283
        gettimeofday(&tv, NULL);
 
284
        if ((*i)->mExpiry.tv_sec < tv.tv_sec ||
 
285
           ((*i)->mExpiry.tv_sec == tv.tv_sec &&
 
286
            (*i)->mExpiry.tv_usec < tv.tv_usec))
 
287
        {
 
288
            if ((*i)->mIsRequest)
 
289
            {
 
290
                DebugLog(<< "Error: timeout waiting for "
 
291
                         << getMethodName((*i)->mMethod) << " method");
 
292
                ++errorCount;
 
293
            }
 
294
            else
 
295
            {
 
296
                DebugLog(<< "Error: timeout waiting for "
 
297
                         << (*i)->mResponseCode << " status code");
 
298
                ++errorCount;
 
299
            }
 
300
            delete *i;
 
301
            WaitQueue.erase(i++);
 
302
        }
 
303
        else
 
304
        {
 
305
            /*
 
306
            cerr << "Still waiting for: ";
 
307
            if ((*i)->mIsRequest)
 
308
            {
 
309
                cerr << MethodNames[(*i)->mMethod] << " method" << endl;
 
310
            }
 
311
            else
 
312
            {
 
313
                cerr << (*i)->mResponseCode << " status code" << endl;
 
314
            }
 
315
            */
 
316
            ++i;
 
317
        }
 
318
    }
 
319
#endif
 
320
 
 
321
    signal(SIGALRM, processTimeouts);
 
322
}
 
323
 
 
324
void
 
325
processInject()
 
326
{
 
327
    const char* start = TestSpecParseBuf->position();
 
328
    const char* now;
 
329
    bool isWireInject = false;
 
330
 
 
331
    if (!strncasecmp(start, "inject_wire", strlen("inject_wire")))
 
332
    {
 
333
        isWireInject = true;
 
334
    }
 
335
    else if (!strncasecmp(start, "inject_tu", strlen("inject_tu")))
 
336
    {
 
337
        isWireInject = false;
 
338
    }
 
339
    else
 
340
    {
 
341
        DebugLog(<< "Warning: error parsing test specification.");
 
342
        TestSpecParseBuf->skipToChar('}');
 
343
        TestSpecParseBuf->skipChar();
 
344
        return;
 
345
    }
 
346
 
 
347
    TestSpecParseBuf->skipToChar('{');
 
348
    TestSpecParseBuf->skipChar();
 
349
    TestSpecParseBuf->skipWhitespace();
 
350
 
 
351
    start = TestSpecParseBuf->position();
 
352
    now = TestSpecParseBuf->skipToChar('}');
 
353
    *const_cast<char*>(now) = 0;
 
354
    DebugLog(<< "Injecting (isWireInject=" << isWireInject << "): " << start);
 
355
    TestSpecParseBuf->skipChar();
 
356
    if (isWireInject)
 
357
    {
 
358
        // sendToWire() is a helper function for TestTransport stuff.
 
359
        // sendToWire(start);
 
360
        SipMessage* message = TestSupport::makeMessage(start, true);
 
361
        assert(message);
 
362
 
 
363
        TestFSM::addMessage(client,message); //  does a client->mStateMacFifo.add(message);
 
364
        
 
365
    }
 
366
    else
 
367
    {
 
368
        SipMessage* message = TestSupport::makeMessage(start, false);
 
369
        assert(message);
 
370
        client->send(*message);
 
371
    }
 
372
}
 
373
 
 
374
void
 
375
processExpect()
 
376
{
 
377
    const char* start = TestSpecParseBuf->position();
 
378
    const char* now;
 
379
    unsigned int expireTime = 1;
 
380
    WaitNode* thisWait = new WaitNode;
 
381
    assert(thisWait);
 
382
    thisWait->mResponseCode = 0;
 
383
 
 
384
    if (!strncasecmp(start, "expect_wire", strlen("expect_wire")))
 
385
    {
 
386
        thisWait->mIsTransport = true;
 
387
    }
 
388
    else if (!strncasecmp(start, "expect_tu", strlen("expect_tu")))
 
389
    {
 
390
        thisWait->mIsTransport = false;
 
391
    }
 
392
    else
 
393
    {
 
394
        DebugLog(<< "Warning: error parsing test specification"); 
 
395
        TestSpecParseBuf->skipToChar('}');
 
396
        TestSpecParseBuf->skipChar();
 
397
        delete thisWait;
 
398
        return;
 
399
    }
 
400
 
 
401
    TestSpecParseBuf->skipToChar('{');
 
402
    TestSpecParseBuf->skipChar();
 
403
    TestSpecParseBuf->skipWhitespace();
 
404
    start = TestSpecParseBuf->position();
 
405
 
 
406
    // We will want to get two of these in an expect_ clause.
 
407
    for (int i = 0; i < 2; i++)
 
408
    {
 
409
        TestSpecParseBuf->skipToChar('=');
 
410
        TestSpecParseBuf->skipChar();
 
411
        TestSpecParseBuf->skipWhitespace();
 
412
        if (!strncasecmp(start, "method", strlen("method")))
 
413
        {
 
414
            start = TestSpecParseBuf->position();
 
415
            now = TestSpecParseBuf->skipToOneOf(ParseBuffer::Whitespace);
 
416
            thisWait->mIsRequest = true;
 
417
            thisWait->mMethod = getMethodType(start, now-start);
 
418
        }
 
419
        else if (!strncasecmp(start, "status", strlen("status")))
 
420
        {
 
421
            TestSpecParseBuf->skipToOneOf("0123456789");
 
422
            thisWait->mIsRequest = false;
 
423
            thisWait->mResponseCode = TestSpecParseBuf->integer();
 
424
        }
 
425
        else if (!strncasecmp(start, "timeout", strlen("timeout")))
 
426
        {
 
427
            TestSpecParseBuf->skipToOneOf("0123456789");
 
428
            expireTime = TestSpecParseBuf->integer();
 
429
        }
 
430
        else
 
431
        {
 
432
            DebugLog(<< "Warning: error parsing test specification"); 
 
433
            TestSpecParseBuf->skipToChar('}');
 
434
            TestSpecParseBuf->skipChar();
 
435
            delete thisWait;
 
436
            return;
 
437
        }
 
438
        TestSpecParseBuf->skipWhitespace();
 
439
        start = TestSpecParseBuf->position();
 
440
    }
 
441
 
 
442
    assert(thisWait);
 
443
 
 
444
    gettimeofday(&thisWait->mExpiry, NULL);
 
445
    thisWait->mExpiry.tv_sec += expireTime / 1000;
 
446
    thisWait->mExpiry.tv_usec += (expireTime % 1000) * 1000;
 
447
    WaitQueue.push_front(thisWait);
 
448
 
 
449
    TestSpecParseBuf->skipToChar('}');
 
450
    TestSpecParseBuf->skipChar();
 
451
 
 
452
    /*
 
453
    cerr << "-> Expecting " << endl;
 
454
    cerr << "   mIsTransport = " << (thisWait->mIsTransport == true) << endl;
 
455
    cerr << "   mIsRequest = " << (thisWait->mIsRequest == true) << endl;
 
456
    cerr << "   mResponseCode = " << (thisWait->mResponseCode) << endl;
 
457
    cerr << "   mExpiry = " << (thisWait->mExpiry.tv_sec) << endl;
 
458
    */
 
459
}
 
460
 
 
461
void
 
462
processDelays()
 
463
{
 
464
    TestSpecParseBuf->skipToChar('{');
 
465
    TestSpecParseBuf->skipChar();
 
466
    TestSpecParseBuf->skipWhitespace();
 
467
 
 
468
    TestSpecParseBuf->skipToOneOf("0123456789");
 
469
    int sleepLength = TestSpecParseBuf->integer();
 
470
 
 
471
    DebugLog( << "Pausing for " << sleepLength << " ms");
 
472
 
 
473
    // We sleep this way to avoid conflict with SIGALRM from alarm().
 
474
    struct timespec ts, remainder;
 
475
    ts.tv_sec = sleepLength / 1000;
 
476
    ts.tv_nsec = (sleepLength % 1000) * 1000000;
 
477
    while (nanosleep(&ts, &remainder) < 0)
 
478
    {
 
479
        ts = remainder;
 
480
    }
 
481
 
 
482
    TestSpecParseBuf->skipToChar('}');
 
483
    TestSpecParseBuf->skipChar();
 
484
}
 
485
 
 
486
bool
 
487
processClause()
 
488
{
 
489
    TestSpecParseBuf->skipWhitespace();
 
490
 
 
491
    // Look for 'i'/'e'/'d' for inject... or expect... or delay respectively.
 
492
    const char* now = TestSpecParseBuf->skipToOneOf("ied#");
 
493
    switch (*now)
 
494
    {
 
495
    case 'i':
 
496
        processInject();
 
497
        break;
 
498
    case 'e':
 
499
        processExpect();
 
500
        break;
 
501
    case 'd':
 
502
        processDelays();
 
503
        break;
 
504
    case '#':
 
505
        TestSpecParseBuf->skipToChar('\n');
 
506
        TestSpecParseBuf->skipChar();
 
507
        break;
 
508
    default:
 
509
        DebugLog(<< "Warning: error parsing test specification");
 
510
        TestSpecParseBuf->skipToChar('}');
 
511
        TestSpecParseBuf->skipChar();
 
512
    }
 
513
 
 
514
    TestSpecParseBuf->skipWhitespace();
 
515
    return !TestSpecParseBuf->eof();
 
516
}
 
517
 
 
518
int
 
519
main(int argc, char *argv[])
 
520
{
 
521
    ProgramName = argv[0];
 
522
 
 
523
    if (NULL == argv[1]) {
 
524
        exitusage();
 
525
    }
 
526
 
 
527
    struct stat buf;
 
528
    if (stat(argv[1], &buf) < 0)
 
529
    {
 
530
        cerr << "Error: " << strerror(errno) << endl;
 
531
        exitusage();
 
532
    }
 
533
 
 
534
    ifstream testSpec;
 
535
    testSpec.open(argv[1], ifstream::in);
 
536
    if (!testSpec.is_open())
 
537
    {
 
538
        cerr << "Error: could not open "<< argv[1] << endl;
 
539
        exitusage();
 
540
    }
 
541
 
 
542
    TestSpecBuf = new char[buf.st_size+1];
 
543
    assert(TestSpecBuf);
 
544
    testSpec.read(TestSpecBuf, buf.st_size);
 
545
    TestSpecParseBuf = new ParseBuffer(TestSpecBuf, buf.st_size);
 
546
    assert(TestSpecParseBuf);
 
547
 
 
548
    int clientFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
549
    clientSa.sin_family = PF_INET;
 
550
    clientSa.sin_addr.s_addr = inet_addr("127.0.0.1");
 
551
    clientSa.sin_port = htons(PORT);
 
552
    int clientFdFlags = fcntl(clientFd, F_GETFL, 0);
 
553
    fcntl(clientFd, F_SETFL, clientFdFlags | O_NONBLOCK);
 
554
 
 
555
    client = new SipStack();
 
556
    assert(client);
 
557
    client->addTransport(UDP, PORT);
 
558
 
 
559
    signal(SIGALRM, processTimeouts);
 
560
 
 
561
    // Cause a signal to be generated with setitimer for its resolution
 
562
    struct itimerval timer;
 
563
    timer.it_value.tv_sec = 0;
 
564
    timer.it_value.tv_usec = 100000; // 100 ms resolution
 
565
    timer.it_interval.tv_sec = 0;
 
566
    timer.it_interval.tv_usec = 100000; // 100 ms resolution
 
567
    setitimer(ITIMER_REAL, &timer, NULL);
 
568
 
 
569
    while (processClause())
 
570
    {
 
571
    }
 
572
 
 
573
    // Catch any remaining events.
 
574
    processTimeouts(0);
 
575
    if (!WaitQueue.empty())
 
576
    {
 
577
        DebugLog( << "Warning: ending with expect clauses outstanding"); 
 
578
        ++errorCount;
 
579
    }
 
580
 
 
581
    if (errorCount > 0)
 
582
    {
 
583
        cerr << "FAIL" << endl;
 
584
    }
 
585
    else
 
586
    {
 
587
        cerr << "PASS" << endl;
 
588
    }
 
589
 
 
590
    return errorCount;
 
591
}
 
592
#else
 
593
#include <iostream>
 
594
int
 
595
main(int argc, char *argv[])
 
596
{
 
597
  std::cout << argv[0] << ": Sorry. This test driver hasn't been ported "
 
598
                          "to Windows yet." << std::endl;
 
599
  return -1;
 
600
}
 
601
#endif
 
602
/* ====================================================================
 
603
 * The Vovida Software License, Version 1.0 
 
604
 * 
 
605
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 
606
 * 
 
607
 * Redistribution and use in source and binary forms, with or without
 
608
 * modification, are permitted provided that the following conditions
 
609
 * are met:
 
610
 * 
 
611
 * 1. Redistributions of source code must retain the above copyright
 
612
 *    notice, this list of conditions and the following disclaimer.
 
613
 * 
 
614
 * 2. Redistributions in binary form must reproduce the above copyright
 
615
 *    notice, this list of conditions and the following disclaimer in
 
616
 *    the documentation and/or other materials provided with the
 
617
 *    distribution.
 
618
 * 
 
619
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 
620
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 
621
 *    not be used to endorse or promote products derived from this
 
622
 *    software without prior written permission. For written
 
623
 *    permission, please contact vocal@vovida.org.
 
624
 *
 
625
 * 4. Products derived from this software may not be called "VOCAL", nor
 
626
 *    may "VOCAL" appear in their name, without prior written
 
627
 *    permission of Vovida Networks, Inc.
 
628
 * 
 
629
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 
630
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
631
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 
632
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 
633
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 
634
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 
635
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
636
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
637
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 
638
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
639
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 
640
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 
641
 * DAMAGE.
 
642
 * 
 
643
 * ====================================================================
 
644
 * 
 
645
 * This software consists of voluntary contributions made by Vovida
 
646
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 
647
 * Inc.  For more information on Vovida Networks, Inc., please see
 
648
 * <http://www.vovida.org/>.
 
649
 *
 
650
 */