reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
FilterStore.cxx
Go to the documentation of this file.
1
2#include "rutil/Logger.hxx"
4#include "rutil/Lock.hxx"
5
8
11
12
13using namespace resip;
14using namespace repro;
15using namespace std;
16
17
18#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
19
20// Note: this code originally used the PCRE (Perl Compatible) regular expression library. The ECMAScript standard is a subset
21// of the Perl regular expression syntax. Posix regular expression syntax is quite a bit different (ie: std::regex_contacts::basic or
22// std::regex_contacts::extended). To be as backwards compatible with existing regular expressions as possible, we want to
23// use the EMCAScript syntax.
24const std::regex_constants::syntax_option_type DefaultFlags = std::regex_constants::ECMAScript;
25
26bool FilterStore::FilterOp::operator<(const FilterOp& rhs) const
27{
28 return filterRecord.mOrder < rhs.filterRecord.mOrder;
29}
30
32 mDb(db)
33{
34 Key key = mDb.firstFilterKey();
35 while ( !key.empty() )
36 {
37 FilterOp filter;
38 filter.filterRecord = mDb.getFilter(key);
39 filter.key = key;
40 filter.pcond1 = 0;
41 filter.pcond2 = 0;
42
43 std::regex_constants::syntax_option_type flags = DefaultFlags;
44 if(filter.filterRecord.mActionData.find("$") == Data::npos)
45 {
46 flags |= std::regex_constants::nosubs;
47 }
48
49 if(!filter.filterRecord.mCondition1Regex.empty())
50 {
51 try
52 {
53 filter.pcond1 = new std::regex(filter.filterRecord.mCondition1Regex.c_str(), flags);
54 }
55 catch (std::regex_error&)
56 {
57 delete filter.pcond1;
58 ErrLog( << "Condition1Regex has invalid match expression: "
59 << filter.filterRecord.mCondition1Regex);
60 filter.pcond1 = 0;
61 }
62 }
63
64 if(!filter.filterRecord.mCondition2Regex.empty())
65 {
66 try
67 {
68 filter.pcond2 = new std::regex(filter.filterRecord.mCondition2Regex.c_str(), flags);
69 }
70 catch (std::regex_error&)
71 {
72 delete filter.pcond2;
73 ErrLog( << "Condition2Regex has invalid match expression: "
74 << filter.filterRecord.mCondition2Regex);
75 filter.pcond2 = 0;
76 }
77 }
78
79 mFilterOperators.insert(filter);
80
81 key = mDb.nextFilterKey();
82 }
83 mCursor = mFilterOperators.begin();
84}
85
87{
88 for(FilterOpList::iterator i = mFilterOperators.begin(); i != mFilterOperators.end(); i++)
89 {
90 if (i->pcond1)
91 {
92 delete i->pcond1;
93 }
94 if (i->pcond2)
95 {
96 delete i->pcond2;
97 }
98 }
99 mFilterOperators.clear();
100}
101
102bool
104 const resip::Data& cond1Regex,
105 const resip::Data& cond2Header,
106 const resip::Data& cond2Regex,
107 const resip::Data& method,
108 const resip::Data& event,
109 short action,
110 const resip::Data& actionData,
111 const short order)
112{
113 InfoLog( << "Add filter" );
114
115 FilterOp filter;
116
117 Key key = buildKey(cond1Header, cond1Regex, cond2Header, cond2Regex, method, event);
118
119 if(findKey(key)) return false;
120
121 filter.filterRecord.mCondition1Header = cond1Header;
122 filter.filterRecord.mCondition1Regex = cond1Regex;
123 filter.filterRecord.mCondition2Header = cond2Header;
124 filter.filterRecord.mCondition2Regex = cond2Regex;
125 filter.filterRecord.mMethod = method;
126 filter.filterRecord.mEvent = event;
127 filter.filterRecord.mAction = action;
128 filter.filterRecord.mActionData = actionData;
129 filter.filterRecord.mOrder = order;
130
131 if(!mDb.addFilter(key , filter.filterRecord))
132 {
133 return false;
134 }
135
136 filter.key = key;
137 filter.pcond1 = 0;
138 filter.pcond2 = 0;
139 std::regex_constants::syntax_option_type flags = DefaultFlags;
140 if(filter.filterRecord.mActionData.find("$") == Data::npos)
141 {
142 flags |= std::regex_constants::nosubs;
143 }
144 if(!filter.filterRecord.mCondition1Regex.empty())
145 {
146 try
147 {
148 filter.pcond1 = new std::regex(filter.filterRecord.mCondition1Regex.c_str(), flags);
149 }
150 catch (std::regex_error&)
151 {
152 delete filter.pcond1;
153 filter.pcond1 = 0;
154 }
155 }
156 if(!filter.filterRecord.mCondition2Regex.empty())
157 {
158 try
159 {
160 filter.pcond2 = new std::regex(filter.filterRecord.mCondition2Regex.c_str(), flags);
161 }
162 catch (std::regex_error&)
163 {
164 delete filter.pcond2;
165 filter.pcond2 = 0;
166 }
167 }
168
169 {
170 WriteLock lock(mMutex);
171 mFilterOperators.insert( filter );
172 }
173 mCursor = mFilterOperators.begin();
174
175 return true;
176}
177
178/*
179AbstractDb::FilterRecordList
180FilterStore::getFilters() const
181{
182 AbstractDb::FilterRecordList result;
183 result.reserve(mFilterOperators.size());
184
185 for (FilterOpList::const_iterator it = mFilterOperators.begin();
186 it != mFilterOperators.end(); it++)
187 {
188 result.push_back(it->filterRecord);
189 }
190 return result;
191}
192*/
193
194void
196 const resip::Data& cond1Regex,
197 const resip::Data& cond2Header,
198 const resip::Data& cond2Regex,
199 const resip::Data& method,
200 const resip::Data& event)
201{
202 Key key = buildKey(cond1Header, cond1Regex, cond2Header, cond2Regex, method, event);
203 eraseFilter(key);
204}
205
206void
208{
209 mDb.eraseFilter(key);
210
211 {
212 WriteLock lock(mMutex);
213
214 FilterOpList::iterator it = mFilterOperators.begin();
215 while (it != mFilterOperators.end())
216 {
217 if (it->key == key)
218 {
219 FilterOpList::iterator i = it;
220 it++;
221 if(i->pcond1)
222 {
223 delete i->pcond1;
224 }
225 if(i->pcond2)
226 {
227 delete i->pcond2;
228 }
229 mFilterOperators.erase(i);
230 }
231 else
232 {
233 it++;
234 }
235 }
236 }
237 mCursor = mFilterOperators.begin(); // reset the cursor since it may have been on deleted filter
238}
239
240bool
242 const resip::Data& cond1Header,
243 const resip::Data& cond1Regex,
244 const resip::Data& cond2Header,
245 const resip::Data& cond2Regex,
246 const resip::Data& method,
247 const resip::Data& event,
248 short action,
249 const resip::Data& actionData,
250 const short order)
251{
252 eraseFilter(originalKey);
253 return addFilter(cond1Header, cond1Regex, cond2Header, cond2Regex, method, event, action, actionData, order);
254}
255
258{
259 ReadLock lock(mMutex);
260
261 mCursor = mFilterOperators.begin();
262 if ( mCursor == mFilterOperators.end() )
263 {
264 return Key( Data::Empty );
265 }
266
267 return mCursor->key;
268}
269
270bool
271FilterStore::findKey(const Key& key)
272{
273 // check if cursor happens to be at the key
274 if ( mCursor != mFilterOperators.end() )
275 {
276 if ( mCursor->key == key )
277 {
278 return true;
279 }
280 }
281
282 // search for the key
283 mCursor = mFilterOperators.begin();
284 while ( mCursor != mFilterOperators.end() )
285 {
286 if ( mCursor->key == key )
287 {
288 return true; // found the key
289 }
290 mCursor++;
291 }
292 return false; // key was not found
293}
294
297{
298 ReadLock lock(mMutex);
299
300 if ( !findKey(key) )
301 {
302 return Key(Data::Empty);
303 }
304
305 mCursor++;
306
307 if ( mCursor == mFilterOperators.end() )
308 {
309 return Key( Data::Empty );
310 }
311
312 return mCursor->key;
313}
314
317{
318 ReadLock lock(mMutex);
319
320 if (!findKey(key))
321 {
323 }
324 return mCursor->filterRecord;
325}
326
327void
328FilterStore::getHeaderFromSipMessage(const SipMessage& msg, const Data& headerName, list<Data>& headerList)
329{
330 // First see if header string is "request-line"
331 if(isEqualNoCase(headerName, "request-line"))
332 {
333 headerList.push_back(Data::from(msg.header(h_RequestLine)));
334 return;
335 }
336
337 // Next check to see if it is a standard header
338 Headers::Type headerType = Headers::getType(headerName.c_str(), headerName.size());
339 if(headerType != Headers::UNKNOWN)
340 {
341 Data headerData;
342 const HeaderFieldValueList* hfv = msg.getRawHeader(headerType);
343 for(HeaderFieldValueList::const_iterator it = hfv->begin(); it != hfv->end(); it++)
344 {
345 it->toShareData(headerData);
346 headerList.push_back(headerData);
347 }
348 }
349 else // Check if custom header
350 {
351 ExtensionHeader exHeader(headerName);
352 if(msg.exists(exHeader))
353 {
354 const StringCategories& exHeaders = msg.header(exHeader);
355 for(StringCategories::const_iterator it = exHeaders.begin(); it != exHeaders.end(); it++)
356 {
357 headerList.push_back(it->value());
358 }
359 }
360 }
361}
362
363bool
364FilterStore::applyRegex(int conditionNum, const Data& header, const Data& match, std::regex *_regex, Data& rewrite)
365{
366 resip_assert(conditionNum < 10);
367
368 std::cmatch matches; // replacements $x1-$x9 are allowed, where x is the condition number
369
370 // Note: Using regex_search instead of regex_match, so that we don't need to fully match
371 // the string, this is backwards compatible with the previous regexec PCRE implementation
372 if(!std::regex_search(header.c_str(), matches, *_regex))
373 {
374 // did not match
375 return false;
376 }
377
378 DebugLog( << " Filter matched: header=" << header << ", regex=" << match);
379
380 if (rewrite.find("$") != Data::npos)
381 {
382 for (int i=1; i<(int)matches.size(); i++)
383 {
384 Data subExp(matches[i]);
385 DebugLog( << " subExpression[" <<i <<"]="<< subExp );
386
387 Data result;
388 {
389 DataStream s(result);
390 ParseBuffer pb(rewrite);
391
392 while (true)
393 {
394 const char* a = pb.position();
395 pb.skipToChars(Data("$") + char('0' + conditionNum) + char('0' + i));
396 if (pb.eof())
397 {
398 s << pb.data(a);
399 break;
400 }
401 else
402 {
403 s << pb.data(a);
404 pb.skipN(3);
405 s << subExp;
406 }
407 }
408 s.flush();
409 }
410 rewrite = result;
411 }
412 }
413 return true;
414}
415
416bool
418 short& action,
419 Data& actionData)
420{
421 if(mFilterOperators.empty()) return false; // If there are no filters bail early to save a few cycles (size check is atomic enough, we don't need a lock)
422
423 ReadLock lock(mMutex);
424
425 Data method(request.methodStr());
426 Data event(request.exists(h_Event) ? request.header(h_Event).value() : Data::Empty);
427
428 for (FilterOpList::iterator it = mFilterOperators.begin();
429 it != mFilterOperators.end(); it++)
430 {
431 const AbstractDb::FilterRecord& rec = it->filterRecord;
432
433 if(!rec.mMethod.empty())
434 {
435 if(!isEqualNoCase(rec.mMethod, method))
436 {
437 DebugLog( << " Skipped - method did not match" );
438 continue;
439 }
440 }
441 if(!rec.mEvent.empty())
442 {
443 if(!isEqualNoCase(rec.mEvent,event))
444 {
445 DebugLog( << " Skipped - event did not match" );
446 continue;
447 }
448 }
449
450 // Get requests SIP headers from SipMessage
451 list<Data> condition1Headers;
452 list<Data> condition2Headers;
453 actionData = rec.mActionData;
454 if(!rec.mCondition1Header.empty() && it->pcond1)
455 {
456 getHeaderFromSipMessage(request, rec.mCondition1Header, condition1Headers);
457
458 // Check condition 1 regex
459 list<Data>::iterator hit = condition1Headers.begin();
460 bool match = false;
461 for(; hit != condition1Headers.end() && match == false; hit++)
462 {
463 match = applyRegex(1, *hit, rec.mCondition1Regex, it->pcond1, actionData);
464 DebugLog( << " Cond1 HeaderName=" << rec.mCondition1Header << ", Value=" << *hit << ", Regex=" << rec.mCondition1Regex << ", match=" << match);
465 }
466 if(!match)
467 {
468 DebugLog( << " Skipped - request did not match first condition: " << request.brief());
469 continue;
470 }
471 }
472 if(!rec.mCondition2Header.empty() && it->pcond2)
473 {
474 getHeaderFromSipMessage(request, rec.mCondition2Header, condition2Headers);
475
476 // Check condition 2 regex
477 list<Data>::iterator hit = condition2Headers.begin();
478 bool match = false;
479 for(; hit != condition2Headers.end() && match == false; hit++)
480 {
481 match = applyRegex(2, *hit, rec.mCondition2Regex, it->pcond2, actionData);
482 DebugLog( << " Cond2 HeaderName=" << rec.mCondition2Header << ", Value=" << *hit << ", Regex=" << rec.mCondition2Regex << ", match=" << match);
483 }
484 if(!match)
485 {
486 DebugLog( << " Skipped - request did not match second condition: " << request.brief());
487 continue;
488 }
489 }
490 // If we make it here Method, Event and both conditions matched - return configured action
491 action = rec.mAction;
492 return true;
493 }
494
495 // If we make it here, then none of the conditions matched - return false
496 return false;
497}
498
499bool
500FilterStore::test(const resip::Data& cond1Header,
501 const resip::Data& cond2Header,
502 short& action,
503 resip::Data& actionData)
504{
505 ReadLock lock(mMutex);
506
507 for (FilterOpList::iterator it = mFilterOperators.begin();
508 it != mFilterOperators.end(); it++)
509 {
510 const AbstractDb::FilterRecord& rec = it->filterRecord;
511 actionData = rec.mActionData;
512
513 // Check condition 1 regex
514 if(!rec.mCondition1Header.empty() && it->pcond1)
515 {
516 if(!applyRegex(1, cond1Header, rec.mCondition1Regex, it->pcond1, actionData))
517 {
518 continue;
519 }
520 }
521
522 // Check condition 2 regex
523 if(!rec.mCondition2Header.empty() && it->pcond2)
524 {
525 if(!applyRegex(2, cond2Header, rec.mCondition2Regex, it->pcond2, actionData))
526 {
527 continue;
528 }
529 }
530
531 // If we make it here both conditions matched - return configured action
532 action = rec.mAction;
533 return true;
534 }
535
536 // If we make it here, then none of the conditions matched - return false
537 return false;
538}
539
541FilterStore::buildKey(const resip::Data& cond1Header,
542 const resip::Data& cond1Regex,
543 const resip::Data& cond2Header,
544 const resip::Data& cond2Regex,
545 const resip::Data& method,
546 const resip::Data& event) const
547{
548 Data pKey = cond1Header + ":" + cond1Regex + ":" +
549 cond2Header + ":" + cond2Regex + ":" +
550 method + ":" + event;
551 return pKey;
552}
553
554
555/* ====================================================================
556 * The Vovida Software License, Version 1.0
557 *
558 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
559 *
560 * Redistribution and use in source and binary forms, with or without
561 * modification, are permitted provided that the following conditions
562 * are met:
563 *
564 * 1. Redistributions of source code must retain the above copyright
565 * notice, this list of conditions and the following disclaimer.
566 *
567 * 2. Redistributions in binary form must reproduce the above copyright
568 * notice, this list of conditions and the following disclaimer in
569 * the documentation and/or other materials provided with the
570 * distribution.
571 *
572 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
573 * and "Vovida Open Communication Application Library (VOCAL)" must
574 * not be used to endorse or promote products derived from this
575 * software without prior written permission. For written
576 * permission, please contact vocal@vovida.org.
577 *
578 * 4. Products derived from this software may not be called "VOCAL", nor
579 * may "VOCAL" appear in their name, without prior written
580 * permission of Vovida Networks, Inc.
581 *
582 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
583 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
584 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
585 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
586 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
587 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
588 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
589 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
590 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
591 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
592 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
593 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
594 * DAMAGE.
595 *
596 * ====================================================================
597 */
const std::regex_constants::syntax_option_type DefaultFlags
ParserContainer< StringCategory > StringCategories
Definition Headers.cxx:228
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
static const struct nv flags[]
Definition adig.c:62
virtual Key nextFilterKey()
virtual bool addFilter(const Key &key, const FilterRecord &rec)
virtual void eraseFilter(const Key &key)
virtual FilterRecord getFilter(const Key &key) const
virtual Key firstFilterKey()
bool updateFilter(const resip::Data &originalKey, const resip::Data &cond1Header, const resip::Data &cond1Regex, const resip::Data &cond2Header, const resip::Data &cond2Regex, const resip::Data &method, const resip::Data &event, short action, const resip::Data &actionData, const short order)
FilterStore(AbstractDb &db)
Key getNextKey(Key &key)
bool addFilter(const resip::Data &cond1Header, const resip::Data &cond1Regex, const resip::Data &cond2Header, const resip::Data &cond2Regex, const resip::Data &method, const resip::Data &event, short action, const resip::Data &actionData, const short order)
void eraseFilter(const resip::Data &cond1Header, const resip::Data &cond1Regex, const resip::Data &cond2Header, const resip::Data &cond2Regex, const resip::Data &method, const resip::Data &event)
bool test(const resip::Data &cond1Header, const resip::Data &cond2Header, short &action, resip::Data &actionData)
bool process(const resip::SipMessage &request, short &action, resip::Data &actionData)
AbstractDb::FilterRecord getFilterRecord(const resip::Data &key)
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
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
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
ListImpl::const_iterator const_iterator
void push_back(const char *buffer, size_t length, bool own)
READ THIS CAREFULLY BEFORE USING THIS FUNCTION.
static Type getType(const char *name, size_t len)
Definition Headers.cxx:351
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
Provides string-parsing functionality with protection from buffer overflow.
Represents a SIP message.
const Data & methodStr() const
Returns a string containing the SIP method for the message.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
const HeaderFieldValueList * getRawHeader(Headers::Type headerType) const
typeless header interface
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