Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/resiprocate/resip/stack/Transport.cxx
Line
Count
Source
1
#if defined(HAVE_CONFIG_H)
2
#include "config.h"
3
#endif
4
5
#include <iostream>
6
7
#if defined(HAVE_SYS_SOCKIO_H)
8
#include <sys/sockio.h>
9
#endif
10
11
#include "rutil/Socket.hxx"
12
#include "rutil/DnsUtil.hxx"
13
#include "rutil/Logger.hxx"
14
#include "rutil/ParseBuffer.hxx"
15
16
#include "resip/stack/ConnectionTerminated.hxx"
17
#include "resip/stack/KeepAlivePong.hxx"
18
#include "resip/stack/Transport.hxx"
19
#include "resip/stack/SipMessage.hxx"
20
#include "resip/stack/TransportFailure.hxx"
21
#include "resip/stack/Helper.hxx"
22
#include "resip/stack/SendData.hxx"
23
#include "rutil/WinLeakCheck.hxx"
24
25
using namespace resip;
26
using namespace std;
27
28
#define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
29
30
void
31
Transport::setSipMessageLoggingHandler(std::shared_ptr<SipMessageLoggingHandler> handler)
32
0
{
33
0
   mSipMessageLoggingHandlers.clear();
34
35
0
   if (handler)
36
0
   {
37
0
      mSipMessageLoggingHandlers.push_back(handler);
38
0
   }
39
0
}
40
41
void
42
Transport::addSipMessageLoggingHandler(std::shared_ptr<SipMessageLoggingHandler> handler)
43
0
{
44
0
   if (handler)
45
0
   {
46
0
      mSipMessageLoggingHandlers.push_back(handler);
47
0
   }
48
0
}
49
50
void
51
Transport::setSipMessageLoggingHandlers(const SipMessageLoggingHandlerList& handlers)
52
0
{
53
0
   mSipMessageLoggingHandlers = handlers;
54
0
}
55
56
void
57
Transport::unsetSipMessageLoggingHandler() noexcept
58
0
{
59
0
   mSipMessageLoggingHandlers.clear();
60
0
}
61
62
Transport::SipMessageLoggingHandlerList
63
Transport::getSipMessageLoggingHandlers() const noexcept
64
0
{
65
0
   return mSipMessageLoggingHandlers;
66
0
}
67
68
Transport::Exception::Exception(const Data& msg, const Data& file, const int line) :
69
0
   BaseException(msg,file,line)
70
0
{
71
0
}
72
73
Transport::Transport(Fifo<TransactionMessage>& rxFifo,
74
                     const GenericIPAddress& address,
75
                     const Data& tlsDomain,
76
                     AfterSocketCreationFuncPtr socketFunc,
77
                     Compression &compression) :
78
0
   mTuple(address),
79
0
   mCongestionManager(nullptr),
80
0
   mStateMachineFifo(rxFifo, 8),
81
0
   mShuttingDown(false),
82
0
   mTlsDomain(tlsDomain),
83
0
   mSocketFunc(socketFunc),
84
0
   mCompression(compression),
85
0
   mTransportFlags(0)
86
0
{
87
#ifdef USE_NETNS
88
   // Needs to be implemented for NETNS
89
   resip_assert(0);
90
#endif
91
0
   mInterface = Tuple::inet_ntop(mTuple);
92
0
}
93
94
Transport::Transport(Fifo<TransactionMessage>& rxFifo,
95
                     int portNum,
96
                     IpVersion version,
97
                     const Data& intfc,
98
                     const Data& tlsDomain,
99
                     AfterSocketCreationFuncPtr socketFunc,
100
                     Compression &compression,
101
                     unsigned transportFlags,
102
                     const Data& netNs) :
103
0
   mInterface(intfc),
104
0
   mTuple(intfc, portNum, version, UNKNOWN_TRANSPORT, Data::Empty, netNs),
105
0
   mCongestionManager(nullptr),
106
0
   mStateMachineFifo(rxFifo,8),
107
0
   mShuttingDown(false),
108
0
   mTlsDomain(tlsDomain),
109
0
   mSocketFunc(socketFunc),
110
0
   mCompression(compression),
111
0
   mTransportFlags(transportFlags)
112
0
{
113
0
}
114
115
Transport::~Transport()
116
0
{
117
0
}
118
119
void
120
Transport::onReload()
121
0
{
122
0
}
123
124
void
125
Transport::drainDscpRefresh()
126
0
{
127
   // Read before the traversal clears the mark state, so a reset still reports
128
   // the 0 it wrote.
129
0
   const int handled = effectiveDscp();
130
131
   // The refresh first, so the report reads the class this pass wrote.
132
0
   if (mDscpRefreshPending.load())
133
0
   {
134
0
      applyDscpToOwnSockets();
135
0
   }
136
137
0
   if (mDscpReportPending.exchange(false))
138
0
   {
139
0
      reportDscp(handled);
140
0
   }
141
0
}
142
143
void
144
Transport::applyDscpToOwnSockets()
145
0
{
146
   // Clear the request before reading the value it was raised for. A setDscp()
147
   // that lands after this either carries the value this pass is about to
148
   // write, or raises the request again for the next cycle.
149
0
   mDscpRefreshPending.store(false);
150
151
0
   const int wanted = mDscp.load();
152
153
   // Before the traversal: with nothing to write and nothing to undo, the
154
   // user's AfterSocketCreationFunc must not be re-invoked at all.
155
0
   if (effectiveDscp(wanted) < 0)
156
0
   {
157
0
      return;
158
0
   }
159
160
0
   invokeAfterSocketCreationFunc();
161
162
   // What this pass wrote, not what mDscp says now: a value that changed while
163
   // the traversal ran has raised its own request.
164
0
   mWasMarkRequested.store(wanted >= 0);
165
0
}
166
167
void
168
Transport::reportDscp(int wanted) const
169
0
{
170
0
   if (wanted < 0)
171
0
   {
172
0
      return;
173
0
   }
174
175
0
   const Socket listener = getListenerSocket();
176
0
   if (listener == INVALID_SOCKET)
177
0
   {
178
0
      return;
179
0
   }
180
181
0
   const int carried = getSocketTos(listener, ipVersion());
182
0
   if (carried < 0)
183
0
   {
184
0
      ErrLog(<< *this << " cannot read the signaling DSCP class back from its listener");
185
0
      return;
186
0
   }
187
188
   // As a class rather than a byte, so the kernel's own ECN bits do not read
189
   // as a mismatch.
190
0
   if ((carried >> 2) != wanted)
191
0
   {
192
0
      ErrLog(<< *this << " asked for signaling DSCP " << wanted
193
0
             << " but its listener carries " << (carried >> 2)
194
0
             << " (ToS byte " << carried << ")");
195
0
      return;
196
0
   }
197
198
0
   InfoLog(<< *this << " signaling DSCP " << (carried >> 2)
199
0
           << " (ToS byte " << carried << ")");
200
0
}
201
202
void
203
Transport::error(int e)
204
0
{
205
0
   if (e != EAGAIN)
206
0
   {
207
0
      InfoLog(<< errorToString(e));
208
0
   }
209
0
}
210
211
Data
212
Transport::errorToString(int e)
213
0
{
214
0
   Data errorString;
215
0
   DataStream ds(errorString);
216
0
   switch (e)
217
0
   {
218
0
      case 0:
219
0
         ds << "None";
220
0
         break;
221
0
      case EAGAIN:
222
0
         ds << "No data ready to read: " << strError(e);
223
0
         break;
224
0
      case EINTR:
225
0
         ds << "The call was interrupted by a signal before any data was read: " << strError(e);
226
0
         break;
227
0
      case EIO:
228
0
         ds << "I/O error: " << strError(e);
229
0
         break;
230
0
      case EBADF:
231
0
         ds << "fd is not a valid file descriptor or is not open for reading: " << strError(e);
232
0
         break;
233
0
      case EINVAL:
234
0
         ds << "fd is attached to an object which is unsuitable for reading: " << strError(e);
235
0
         break;
236
0
      case EFAULT:
237
0
         ds << "buf is outside your accessible address space: " << strError(e);
238
0
         break;
239
240
#if defined(WIN32)
241
      case WSAENETDOWN:
242
         ds << "The network subsystem has failed.";
243
         break;
244
      case WSAEFAULT:
245
         ds << "The buf or from parameters are not part of the user address space, "
246
               "or the fromlen parameter is too small to accommodate the peer address.";
247
         break;
248
      case WSAEINTR:
249
         ds << "The (blocking) call was canceled through WSACancelBlockingCall.";
250
         break;
251
      case WSAEINPROGRESS:
252
         ds << "A blocking Windows Sockets 1.1 call is in progress, or the "
253
               "service provider is still processing a callback function.";
254
         break;
255
      case WSAEINVAL:
256
         ds << "The socket has not been bound with bind, or an unknown flag was specified, "
257
               "or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, "
258
               "or (for byte stream-style sockets only) len was zero or negative.";
259
         break;
260
      case WSAEISCONN :
261
         ds << "The socket is connected. This function is not permitted with a connected socket, "
262
               "whether the socket is connection-oriented or connectionless.";
263
         break;
264
      case WSAENETRESET:
265
         ds << "The connection has been broken due to the keep-alive activity "
266
               "detecting a failure while the operation was in progress.";
267
         break;
268
      case WSAENOTSOCK:
269
         ds << "The descriptor is not a socket.";
270
         break;
271
      case WSAEOPNOTSUPP:
272
         ds << " MSG_OOB was specified, but the socket is not stream-style such as type "
273
               "SOCK_STREAM, OOB data is not supported in the communication domain associated with this socket, "
274
               "or the socket is unidirectional and supports only send operations.";
275
         break;
276
      case WSAESHUTDOWN:
277
         ds << "The socket has been shut down; it is not possible to recvfrom on a socket after "
278
               "shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.";
279
         break;
280
      case WSAEMSGSIZE:
281
         ds << "The message was too large to fit into the specified buffer and was truncated.";
282
         break;
283
      case WSAETIMEDOUT:
284
         ds << "The connection has been dropped, because of a network failure or because the "
285
               "system on the other end went down without notice.";
286
         break;
287
      case WSAECONNRESET :
288
         ds << "Connection reset.";
289
         break;
290
      case WSAEWOULDBLOCK:
291
         ds << "Would block.";
292
         break;
293
      case WSAEHOSTUNREACH:
294
         ds << "A socket operation was attempted to an unreachable host.";
295
         break;
296
      case WSANOTINITIALISED:
297
         ds << "Either the application has not called WSAStartup or WSAStartup failed. "
298
               "The application may be accessing a socket that the current active task does not own (that is, trying to share a socket between tasks),"
299
               "or WSACleanup has been called too many times.";
300
         break;
301
      case WSAEACCES:
302
         ds << "An attempt was made to access a socket in a way forbidden by its access permissions.";
303
         break;
304
      case WSAENOBUFS:
305
         ds << "An operation on a socket could not be performed because the system lacked sufficient "
306
               "buffer space or because a queue was full.";
307
         break;
308
      case WSAENOTCONN:
309
         ds << "A request to send or receive data was disallowed because the socket is not connected "
310
               "and (when sending on a datagram socket using sendto) no address was supplied.";
311
         break;
312
      case WSAECONNABORTED:
313
         ds << "An established connection was aborted by the software in your host computer, possibly "
314
               "due to a data transmission time-out or protocol error.";
315
         break;
316
      case WSAEADDRNOTAVAIL:
317
         ds << "The requested address is not valid in its context. This normally results from an attempt to "
318
               "bind to an address that is not valid for the local computer.";
319
         break;
320
      case WSAEAFNOSUPPORT:
321
         ds << "An address incompatible with the requested protocol was used.";
322
         break;
323
      case WSAEDESTADDRREQ:
324
         ds << "A required address was omitted from an operation on a socket.";
325
         break;
326
      case WSAENETUNREACH:
327
         ds << "A socket operation was attempted to an unreachable network.";
328
         break;
329
#endif
330
331
0
      default:
332
0
         ds << "Some other error (" << e << "): " << strError(e);
333
0
         break;
334
0
   }
335
0
   ds.flush();
336
0
   return errorString;
337
0
}
338
339
void
340
Transport::flowTerminated(const Tuple& flow, TransportFailure::FailureReason failureReason, int failureSubCode, const Data& failureString, const std::list<Data> additionalFailureStrings)
341
0
{
342
0
   mStateMachineFifo.add(new ConnectionTerminated(flow, failureReason, failureSubCode, failureString, additionalFailureStrings));
343
0
}
344
345
void
346
Transport::keepAlivePong(const Tuple& flow)
347
0
{
348
0
   mStateMachineFifo.add(new KeepAlivePong(flow));
349
0
}
350
351
void
352
Transport::fail(const Data& tid, TransportFailure::FailureReason reason, int subCode)
353
0
{
354
0
   if (!tid.empty())
355
0
   {
356
0
      mStateMachineFifo.add(new TransportFailure(tid, reason, subCode));
357
0
   }
358
0
}
359
360
void 
361
Transport::setTcpConnectState(const Data& tid, TcpConnectState::State state)
362
0
{
363
0
    if (!tid.empty())
364
0
    {
365
0
        mStateMachineFifo.add(new TcpConnectState(tid, state));
366
0
    }
367
0
}
368
369
std::unique_ptr<SendData>
370
Transport::makeSendData( const Tuple& dest, const Data& d, const Data& tid, const Data &sigcompId)
371
0
{
372
0
   resip_assert(dest.getPort() != -1);
373
0
   std::unique_ptr<SendData> data(new SendData(dest, d, tid, sigcompId));
374
0
   return data;
375
0
}
376
377
void
378
Transport::makeFailedResponse(const SipMessage& msg,
379
                              int responseCode,
380
                              const char * warning)
381
0
{
382
0
  if (msg.isResponse()) return;
383
384
0
  const Tuple& dest = msg.getSource();
385
386
0
  std::unique_ptr<SipMessage> errMsg(Helper::makeResponse(msg,
387
0
                                                        responseCode,
388
0
                                                        warning ? warning : "Original request had no Vias"));
389
390
  // make send data here w/ blank tid and blast it out.
391
  // encode message
392
0
  Data encoded;
393
0
  encoded.clear();
394
0
  DataStream encodeStream(encoded);
395
0
  errMsg->encode(encodeStream);
396
0
  encodeStream.flush();
397
0
  resip_assert(!encoded.empty());
398
399
0
  InfoLog(<<"Sending response directly to " << dest << " : " << errMsg->brief() );
400
401
  // Calculate compartment ID for outbound message
402
0
  Data remoteSigcompId;
403
0
   setRemoteSigcompId(*errMsg,remoteSigcompId);
404
0
  send(makeSendData(dest, encoded, Data::Empty, remoteSigcompId));
405
0
}
406
407
std::unique_ptr<SendData>
408
Transport::make503(SipMessage& msg, uint16_t retryAfter)
409
0
{
410
0
  std::unique_ptr<SendData> result;
411
0
  if (msg.isResponse()) return result;
412
413
0
   try
414
0
   {
415
0
      if(msg.method()==ACK)
416
0
      {
417
0
         return result;
418
0
      }
419
0
   }
420
0
   catch(BaseException&)
421
0
   {
422
      // .bwc. Parse failed on the start-line. Stop.
423
0
      return result;
424
0
   }
425
   
426
0
  const Tuple& dest = msg.getSource();
427
428
   // Calculate compartment ID for outbound message
429
0
   Data remoteSigcompId;
430
0
   setRemoteSigcompId(msg,remoteSigcompId);
431
432
   // .bwc. msg is completely unverified. Handle with caution.
433
0
   result=makeSendData(dest, Data::Empty, Data::Empty, remoteSigcompId);
434
0
   static const Data retryAfterHeader("Retry-After: ");
435
0
   Data value(retryAfter);
436
0
   Helper::makeRawResponse(result->data, msg, 503, retryAfterHeader+value+"\r\n");
437
438
0
  return result;
439
0
}
440
441
std::unique_ptr<SendData>
442
Transport::make100(SipMessage& msg)
443
0
{
444
0
  std::unique_ptr<SendData> result;
445
0
  if (msg.isResponse()) return result;
446
447
0
   try
448
0
   {
449
0
      if(msg.method()==ACK)
450
0
      {
451
0
         return result;
452
0
      }
453
0
   }
454
0
   catch(BaseException&)
455
0
   {
456
      // .bwc. Parse failed on the start-line. Stop.
457
0
      return result;
458
0
   }
459
   
460
0
  const Tuple& dest = msg.getSource();
461
462
   // Calculate compartment ID for outbound message
463
0
   Data remoteSigcompId;
464
0
   setRemoteSigcompId(msg,remoteSigcompId);
465
466
   // .bwc. msg is completely unverified. Handle with caution.
467
0
   result=makeSendData(dest, Data::Empty, Data::Empty, remoteSigcompId);
468
0
   Helper::makeRawResponse(result->data, msg, 100);
469
470
0
   return result;
471
0
}
472
473
void
474
Transport::setRemoteSigcompId(SipMessage& msg, Data& remoteSigcompId)
475
0
{
476
0
   if (mCompression.isEnabled())
477
0
   {
478
0
      try
479
0
      {
480
0
         const Via &topVia(msg.const_header(h_Vias).front());
481
         
482
0
         if(topVia.exists(p_comp) && topVia.param(p_comp) == "sigcomp")
483
0
         {
484
0
            if (topVia.exists(p_sigcompId))
485
0
            {
486
0
               remoteSigcompId = topVia.param(p_sigcompId);
487
0
            }
488
0
            else
489
0
            {
490
               // XXX rohc-sigcomp-sip-03 says "sent-by",
491
               // but this should probably be "received" if present,
492
               // and "sent-by" otherwise.
493
               // XXX Also, the spec is ambiguous about whether
494
               // to include the port in this identifier.
495
0
               remoteSigcompId = topVia.sentHost();
496
0
            }
497
0
         }
498
0
      }
499
0
      catch(BaseException&)
500
0
      {
501
         // ?bwc? Couldn't grab sigcomp compartment id. We don't even know if
502
         // the initial request was using sigcomp or not. 
503
         // What should we do here?
504
0
      }
505
0
   }
506
0
}
507
508
void
509
Transport::stampReceived(SipMessage* message)
510
0
{
511
   // set the received= and rport= parameters in the message if necessary !jf!
512
0
   if (message->isRequest() && message->exists(h_Vias) && !message->const_header(h_Vias).empty())
513
0
   {
514
0
      const Tuple& tuple = message->getSource();
515
0
      Data received = Tuple::inet_ntop(tuple);
516
0
      if(message->const_header(h_Vias).front().sentHost() != received)  // only add if received address is different from sent-by in Via
517
0
      {
518
0
         message->header(h_Vias).front().param(p_received) = received;
519
0
      }
520
      //message->header(h_Vias).front().param(p_received) = Tuple::inet_ntop(tuple);
521
0
      if (message->const_header(h_Vias).front().exists(p_rport))
522
0
      {
523
0
         message->header(h_Vias).front().param(p_rport).port() = tuple.getPort();
524
0
      }
525
0
   }
526
0
   DebugLog (<< "incoming from: " << message->getSource());
527
0
   StackLog (<< endl << endl << *message);
528
0
}
529
530
bool
531
Transport::basicCheck(const SipMessage& msg)
532
0
{
533
0
   resip::Data reason;
534
0
   if (msg.isExternal())
535
0
   {
536
0
      try
537
0
      {
538
0
         if (!Helper::validateMessage(msg,&reason))
539
0
         {
540
0
            InfoLog(<<"Message Failed basicCheck :" << msg.brief());
541
0
            if (msg.isRequest() && msg.method()!=ACK )
542
0
            {
543
               // this is VERY low-level b/c we don't have a transaction...
544
               // here we make a response to warn the offending party.
545
0
               makeFailedResponse(msg,400,reason.c_str());
546
0
            }
547
0
            return false;
548
0
         }
549
0
         else if (mShuttingDown && msg.isRequest() && msg.method() != ACK)
550
0
         {
551
0
            InfoLog (<< "Server has been shutdown, reject message with 503");
552
            // this is VERY low-level b/c we don't have a transaction...
553
            // here we make a response to warn the offending party.
554
0
            makeFailedResponse(msg, 503, "Server has been shutdown");
555
0
            return false;
556
0
         }
557
0
      }
558
0
      catch (BaseException& e)
559
0
      {
560
0
         InfoLog (<< "Cannot make failure response to badly constructed message: " << e);
561
0
         return false;
562
0
      }
563
0
   }
564
0
   return true;
565
0
}
566
567
bool
568
Transport::setDscp(int dscp)
569
0
{
570
   // Refused once per change, not once per accepted connection.
571
0
   if (dscp < -1 || dscp > 63)
572
0
   {
573
0
      ErrLog(<< *this << " refusing DSCP class " << dscp << ", outside -1..63");
574
0
      return false;
575
0
   }
576
577
0
   if (mDscp.exchange(dscp) != dscp)
578
0
   {
579
0
      mDscpRefreshPending.store(true);
580
0
   }
581
582
   // Raised on every call, not only on a change: a marking that failed is then
583
   // reported again on the next reload rather than reading as fixed.
584
0
   mDscpReportPending.store(true);
585
586
0
   return true;
587
0
}
588
589
int
590
Transport::effectiveDscp() const
591
0
{
592
0
   return effectiveDscp(mDscp.load());
593
0
}
594
595
int
596
Transport::effectiveDscp(int wanted) const
597
0
{
598
0
   if (wanted >= 0)
599
0
   {
600
0
      return wanted;
601
0
   }
602
603
0
   return mWasMarkRequested.load() ? 0 : -1;
604
0
}
605
606
void
607
Transport::applySocketOptions(Socket sock) const
608
0
{
609
0
   const int dscp = effectiveDscp();
610
0
   if (dscp >= 0)
611
0
   {
612
      // The return is dropped on purpose: the listener read-back reports the
613
      // state once, instead of once per socket a refresh touches.
614
0
      setSocketDscp(sock, dscp, ipVersion());
615
0
      mWasMarkRequested.store(true);
616
0
   }
617
618
0
   callSocketFunc(sock);
619
0
}
620
621
void
622
Transport::callSocketFunc(Socket sock) const
623
0
{
624
0
   if (mSocketFunc)
625
0
   {
626
0
      mSocketFunc(sock, transport(), __FILE__, __LINE__);
627
0
   }
628
0
}
629
630
void
631
Transport::pushRxMsgUp(SipMessage* message)
632
0
{
633
0
   for(auto& handler : getSipMessageLoggingHandlers())
634
0
   {
635
0
       handler->inboundMessage(message->getSource(), message->getReceivedTransportTuple(), *message);
636
0
   }
637
638
0
   mStateMachineFifo.add(message);
639
0
}
640
641
bool
642
Transport::operator==(const Transport& rhs) const
643
0
{
644
0
   return ( ( mTuple.isV4() == rhs.isV4()) &&
645
0
            ( port() == rhs.port()) &&
646
0
            ( memcmp(&boundInterface(),&rhs.boundInterface(),mTuple.length()) == 0) );
647
0
}
648
649
EncodeStream&
650
resip::operator<<(EncodeStream& strm, const resip::Transport& rhs)
651
0
{
652
0
   strm << "Transport: " << rhs.mTuple;
653
0
   if (rhs.hasSpecificContact() && !rhs.mInterface.empty()) strm << " on " << rhs.mInterface;
654
0
   if (!rhs.mTlsDomain.empty()) strm << " (tlsdomain=" << rhs.mTlsDomain << ")";
655
0
   return strm;
656
0
}
657
658
/* ====================================================================
659
 * The Vovida Software License, Version 1.0
660
 *
661
 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
662
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
663
 *
664
 * Redistribution and use in source and binary forms, with or without
665
 * modification, are permitted provided that the following conditions
666
 * are met:
667
 *
668
 * 1. Redistributions of source code must retain the above copyright
669
 *    notice, this list of conditions and the following disclaimer.
670
 *
671
 * 2. Redistributions in binary form must reproduce the above copyright
672
 *    notice, this list of conditions and the following disclaimer in
673
 *    the documentation and/or other materials provided with the
674
 *    distribution.
675
 *
676
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
677
 *    and "Vovida Open Communication Application Library (VOCAL)" must
678
 *    not be used to endorse or promote products derived from this
679
 *    software without prior written permission. For written
680
 *    permission, please contact vocal@vovida.org.
681
 *
682
 * 4. Products derived from this software may not be called "VOCAL", nor
683
 *    may "VOCAL" appear in their name, without prior written
684
 *    permission of Vovida Networks, Inc.
685
 *
686
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
687
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
688
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
689
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
690
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
691
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
692
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
693
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
694
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
695
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
696
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
697
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
698
 * DAMAGE.
699
 *
700
 * ====================================================================
701
 *
702
 * This software consists of voluntary contributions made by Vovida
703
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
704
 * Inc.  For more information on Vovida Networks, Inc., please see
705
 * <http://www.vovida.org/>.
706
 *
707
 * vi: set shiftwidth=3 expandtab:
708
 */