reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
RouteStore.cxx
Go to the documentation of this file.
1
2#include "rutil/Logger.hxx"
4#include "rutil/Lock.hxx"
5#include "resip/stack/Uri.hxx"
6
9
10
11using namespace resip;
12using namespace repro;
13using namespace std;
14
15#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
16
17// Note: this code originally used the PCRE (Perl Compatible) regular expression library. The ECMAScript standard is a subset
18// of the Perl regular expression syntax. Posix regular expression syntax is quite a bit different (ie: std::regex_contacts::basic or
19// std::regex_contacts::extended). To be as backwards compatible with existing regular expressions as possible, we want to
20// use the EMCAScript syntax.
21const std::regex_constants::syntax_option_type DefaultFlags = std::regex_constants::ECMAScript;
22
23bool RouteStore::RouteOp::operator<(const RouteOp& rhs) const
24{
25 return routeRecord.mOrder < rhs.routeRecord.mOrder;
26}
27
29 mDb(db)
30{
31 Key key = mDb.firstRouteKey();
32 while ( !key.empty() )
33 {
34 RouteOp route;
35 route.routeRecord = mDb.getRoute(key);
36
37 route.key = key;
38 route.preq = 0;
39
40 if(!route.routeRecord.mMatchingPattern.empty())
41 {
42 std::regex_constants::syntax_option_type flags = DefaultFlags;
43 if(route.routeRecord.mRewriteExpression.find("$") == Data::npos)
44 {
45 flags |= std::regex_constants::nosubs;
46 }
47 try
48 {
49 route.preq = new std::regex(route.routeRecord.mMatchingPattern.c_str(), flags);
50 }
51 catch (std::regex_error& e)
52 {
53 delete route.preq;
54 ErrLog(<< "Routing rule has invalid match expression: "
55 << route.routeRecord.mMatchingPattern
56 << ", ex=" << e.what());
57 route.preq = 0;
58 }
59 }
60
61 mRouteOperators.insert( route );
62
63 key = mDb.nextRouteKey();
64 }
65 // Now that everything is read in - see if we need to upgrade any entries
66 // if route key is old and doesn't contain order, then upgrade it in db
67 for (RouteOpList::iterator it = mRouteOperators.begin(); it != mRouteOperators.end();)
68 {
69 if (!it->key.prefix(";"))
70 {
71 // This will delete, gen a new key and add - essentially updating the key to the new format
72 AbstractDb::RouteRecord rec = it->routeRecord; // Need to use copies, since this method deletes the original data
73 updateRoute(it->key, rec.mMethod, rec.mEvent, rec.mMatchingPattern, rec.mRewriteExpression, rec.mOrder);
74 // Iterator is now invalidated - reset to begin() - yes this is inefficient, but it only happens once on upgrade
75 it = mRouteOperators.begin();
76 }
77 else
78 {
79 it++;
80 }
81 }
82
83 // Initialize cursor to the start
84 mCursor = mRouteOperators.begin();
85}
86
88{
89 for(RouteOpList::iterator i = mRouteOperators.begin(); i != mRouteOperators.end(); i++)
90 {
91 if ( i->preq )
92 {
93 delete i->preq;
94
95 // !abr! Can't modify elements in a set
96 // i->preq = 0;
97 }
98 }
99 mRouteOperators.clear();
100}
101
102bool
104 const resip::Data& event,
105 const resip::Data& matchingPattern,
106 const resip::Data& rewriteExpression,
107 const short order )
108{
109 InfoLog( << "Add route" );
110
111 RouteOp route;
112
113 Key key = buildKey(method, event, matchingPattern, order);
114
115 if(findKey(key)) return false;
116
117 route.routeRecord.mMethod = method;
118 route.routeRecord.mEvent = event;
119 route.routeRecord.mMatchingPattern = matchingPattern;
120 route.routeRecord.mRewriteExpression = rewriteExpression;
121 route.routeRecord.mOrder = order;
122
123 if(!mDb.addRoute(key, route.routeRecord))
124 {
125 return false;
126 }
127
128 route.key = key;
129 route.preq = 0;
130 if (!route.routeRecord.mMatchingPattern.empty())
131 {
132 std::regex_constants::syntax_option_type flags = DefaultFlags;
133 if (route.routeRecord.mRewriteExpression.find("$") == Data::npos)
134 {
135 flags |= std::regex_constants::nosubs;
136 }
137 try
138 {
139 route.preq = new std::regex(route.routeRecord.mMatchingPattern.c_str(), flags);
140 }
141 catch (std::regex_error& e)
142 {
143 delete route.preq;
144 ErrLog(<< "Routing rule has invalid match expression: "
145 << route.routeRecord.mMatchingPattern
146 << ", ex=" << e.what());
147 route.preq = 0;
148 }
149 }
150
151 {
152 WriteLock lock(mMutex);
153 mRouteOperators.insert( route );
154 }
155 mCursor = mRouteOperators.begin();
156
157 return true;
158}
159
160/*
161AbstractDb::RouteRecordList
162RouteStore::getRoutes() const
163{
164 AbstractDb::RouteRecordList result;
165 result.reserve(mRouteOperators.size());
166
167 for (RouteOpList::const_iterator it = mRouteOperators.begin();
168 it != mRouteOperators.end(); it++)
169 {
170 result.push_back(it->routeRecord);
171 }
172 return result;
173}
174*/
175
176void
178 const resip::Data& event,
179 const resip::Data& matchingPattern,
180 const short order)
181{
182 Key key = buildKey(method, event, matchingPattern, order);
183 eraseRoute(key);
184}
185
186void
188{
189 mDb.eraseRoute(key);
190
191 {
192 WriteLock lock(mMutex);
193
194 RouteOpList::iterator it = mRouteOperators.begin();
195 while ( it != mRouteOperators.end() )
196 {
197 if (it->key == key )
198 {
199 RouteOpList::iterator i = it;
200 it++;
201 if ( i->preq )
202 {
203 delete i->preq;
204
205 // !abr! Can't modify elements in a set
206 //i->preq = 0;
207 }
208 mRouteOperators.erase(i);
209 }
210 else
211 {
212 it++;
213 }
214 }
215 }
216 mCursor = mRouteOperators.begin(); // reset the cursor since it may have been on deleted route
217}
218
219bool
221 const resip::Data& method,
222 const resip::Data& event,
223 const resip::Data& matchingPattern,
224 const resip::Data& rewriteExpression,
225 const short order)
226{
227 eraseRoute(originalKey);
228 return addRoute(method, event, matchingPattern, rewriteExpression, order);
229}
230
233{
234 ReadLock lock(mMutex);
235
236 mCursor = mRouteOperators.begin();
237 if ( mCursor == mRouteOperators.end() )
238 {
239 return Key( Data::Empty );
240 }
241
242 return mCursor->key;
243}
244
245bool
246RouteStore::findKey(const Key& key)
247{
248 // check if cursor happens to be at the key
249 if ( mCursor != mRouteOperators.end() )
250 {
251 if ( mCursor->key == key )
252 {
253 return true;
254 }
255 }
256
257 // search for the key
258 mCursor = mRouteOperators.begin();
259 while ( mCursor != mRouteOperators.end() )
260 {
261 if ( mCursor->key == key )
262 {
263 return true; // found the key
264 }
265 mCursor++;
266 }
267 return false; // key was not found
268}
269
272{
273 ReadLock lock(mMutex);
274
275 if ( !findKey(key) )
276 {
277 return Key(Data::Empty);
278 }
279
280 mCursor++;
281
282 if ( mCursor == mRouteOperators.end() )
283 {
284 return Key( Data::Empty );
285 }
286
287 return mCursor->key;
288}
289
292{
293 ReadLock lock(mMutex);
294
295 if (!findKey(key))
296 {
298 }
299 return mCursor->routeRecord;
300}
301
304 const resip::Data& method,
305 const resip::Data& event)
306{
307 RouteStore::UriList targetSet;
308 if(mRouteOperators.empty()) return targetSet; // If there are no routes bail early to save a few cycles (size check is atomic enough, we don't need a lock)
309
310 Data uri = Data::from(ruri);
311
312 ReadLock lock(mMutex);
313
314 for (RouteOpList::iterator it = mRouteOperators.begin();
315 it != mRouteOperators.end(); it++)
316 {
317 DebugLog( << "Consider route " // << *it
318 << " reqUri=" << ruri
319 << " method=" << method
320 << " event=" << event );
321
322 const AbstractDb::RouteRecord& rec = it->routeRecord;
323
324 if(!rec.mMethod.empty())
325 {
326 if(!isEqualNoCase(rec.mMethod,method))
327 {
328 DebugLog( << " Skipped - method did not match" );
329 continue;
330 }
331
332 }
333 if(!rec.mEvent.empty())
334 {
335 if(!isEqualNoCase(rec.mEvent, event))
336 {
337 DebugLog( << " Skipped - event did not match" );
338 continue;
339 }
340 }
341 const Data& rewrite = rec.mRewriteExpression;
342 const Data& match = rec.mMatchingPattern;
343 if ( it->preq )
344 {
345 std::cmatch matches;
346
347 // Note: Using regex_search instead of regex_match, so that we don't need to fully match
348 // the string, this is backwards compatible with the previous regexec PCRE implementation
349 if(!std::regex_search(uri.c_str(), matches, *it->preq))
350 {
351 // did not match
352 DebugLog( << " Skipped - request URI "<< uri << " did not match " << match );
353 continue;
354 }
355
356 DebugLog( << " Route matched" );
357 Data target = rewrite;
358
359 if ( rewrite.find("$") != Data::npos )
360 {
361 for ( int i=1; i<(int)matches.size(); i++)
362 {
363 Data subExp(matches[i]);
364 DebugLog( << " subExpression[" <<i <<"]="<< subExp );
365
366 Data result;
367 {
368 DataStream s(result);
369
370 ParseBuffer pb(target);
371
372 while (true)
373 {
374 const char* a = pb.position();
375 pb.skipToChars( Data("$") + char('0'+i) );
376 if ( pb.eof() )
377 {
378 s << pb.data(a);
379 break;
380 }
381 else
382 {
383 s << pb.data(a);
384 pb.skipN(2);
385 s << subExp;
386 }
387 }
388 s.flush();
389 }
390 target = result;
391 }
392 }
393
394 Uri targetUri;
395 try
396 {
397 targetUri = Uri(target);
398 }
399 catch( BaseException& )
400 {
401 ErrLog( << "Routing rule transform " << rewrite << " gave invalid URI " << target );
402 try
403 {
404 targetUri = Uri( Data("sip:")+target);
405 }
406 catch( BaseException& )
407 {
408 ErrLog( << "Routing rule transform " << rewrite << " gave invalid URI sip:" << target );
409 continue;
410 }
411 }
412 targetSet.push_back( targetUri );
413 }
414 }
415
416 return targetSet;
417}
418
420RouteStore::buildKey(const resip::Data& method,
421 const resip::Data& event,
422 const resip::Data& matchingPattern,
423 const short order) const
424{
425 // note: Using ; at the start so we can tell if the key is the old format or not.
426 // Old format keys did not contain the order in the key and thus wouldn't allow
427 // multiple entries with the same method,event and matchingPattern. This is desirable in
428 // cases where you want multiple destinations for a particular route.
429 // Old Format: Data pKey = method+":"+event+":"+matchingPattern;
430 Data pKey = ";" + Data((int32_t)order) + ":" + method + " : " + event + " : " + matchingPattern;
431 return pKey;
432}
433
434
435/* ====================================================================
436 * The Vovida Software License, Version 1.0
437 *
438 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
439 *
440 * Redistribution and use in source and binary forms, with or without
441 * modification, are permitted provided that the following conditions
442 * are met:
443 *
444 * 1. Redistributions of source code must retain the above copyright
445 * notice, this list of conditions and the following disclaimer.
446 *
447 * 2. Redistributions in binary form must reproduce the above copyright
448 * notice, this list of conditions and the following disclaimer in
449 * the documentation and/or other materials provided with the
450 * distribution.
451 *
452 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
453 * and "Vovida Open Communication Application Library (VOCAL)" must
454 * not be used to endorse or promote products derived from this
455 * software without prior written permission. For written
456 * permission, please contact vocal@vovida.org.
457 *
458 * 4. Products derived from this software may not be called "VOCAL", nor
459 * may "VOCAL" appear in their name, without prior written
460 * permission of Vovida Networks, Inc.
461 *
462 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
463 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
464 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
465 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
466 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
467 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
468 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
469 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
470 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
471 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
472 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
473 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
474 * DAMAGE.
475 *
476 * ====================================================================
477 */
const std::regex_constants::syntax_option_type DefaultFlags
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
const std::regex_constants::syntax_option_type DefaultFlags
static const struct nv flags[]
Definition adig.c:62
virtual RouteRecord getRoute(const Key &key) const
virtual void eraseRoute(const Key &key)
virtual Key firstRouteKey()
virtual Key nextRouteKey()
virtual bool addRoute(const Key &key, const RouteRecord &rec)
std::vector< resip::Uri > UriList
UriList process(const resip::Uri &ruri, const resip::Data &method, const resip::Data &event)
bool addRoute(const resip::Data &method, const resip::Data &event, const resip::Data &matchingPattern, const resip::Data &rewriteExpression, const short order)
AbstractDb::RouteRecord getRouteRecord(const resip::Data &key)
resip::Data Key
Key getNextKey(Key &key)
void eraseRoute(const resip::Data &method, const resip::Data &event, const resip::Data &matchingPattern, const short order)
RouteStore(AbstractDb &db)
bool updateRoute(const resip::Data &originalKey, const resip::Data &method, const resip::Data &event, const resip::Data &matchingPattern, const resip::Data &rewriteExpression, const short order)
The abstract base-class for all exceptions thrown by resip code.
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
static Data from(const T &x)
Converts from arbitrary other type to Data.
Definition Data.hxx:398
size_type find(const Data &match, size_type start=0) const
Finds a specified sequence of bytes in this Data.
Definition Data.cxx:1947
static const size_type npos
Represents an impossible position; returned to indicate failure to find.
Definition Data.hxx:887
Provides string-parsing functionality with protection from buffer overflow.
CurrentPosition skipToChars(const char *cs)
CurrentPosition position() const
CurrentPosition skipN(size_t count)
void data(Data &data, const char *start) const
make the passed in data share memory with the buffer (uses Data::Share)
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
signed int int32_t
Definition stdint.h:82