Browse code

sip: destination blacklist

When a destination cannot be reached (timeout+grace period), the IP/port tuple is blacklisted for a certain period of time (default_bl_ttl). if all destinations for a message are blacklisted, a 500 error reply is generated internally.

In case a downstream proxy or UA returns 503, this proxy or UA also gets blacklisted.

Raphael Coeffic authored on 13/09/2013 10:01:03
Showing 11 changed files
... ...
@@ -690,13 +690,20 @@ int AmBasicSipDialog::sendRequest(const string& method,
690 690
       req.hdrs += SIP_HDR_COLSP(SIP_HDR_USER_AGENT) + AmConfig::Signature + CRLF;
691 691
   }
692 692
 
693
+  int send_flags = 0;
694
+  if(patch_ruri_next_hop && remote_tag.empty()) {
695
+    send_flags |= TR_FLAG_NEXT_HOP_RURI;
696
+  }
697
+
698
+  if(flags & SIP_FLAGS_NOBL) {
699
+    send_flags |= TR_FLAG_DISABLE_BL;
700
+  }
701
+
693 702
   int res = SipCtrlInterface::send(req, local_tag,
694 703
 				   remote_tag.empty() || !next_hop_1st_req ?
695 704
 				   next_hop : "",
696 705
 				   outbound_interface,
697
-				   !patch_ruri_next_hop || !remote_tag.empty() ? 0
698
-				   : SEND_REQUEST_FLAG_NEXT_HOP_RURI,
699
-				   logger);
706
+				   send_flags,logger);
700 707
   if(res) {
701 708
     ERROR("Could not send request: method=%s; call-id=%s; cseq=%i\n",
702 709
 	  req.method.c_str(),req.callid.c_str(),req.cseq);
... ...
@@ -43,6 +43,8 @@ using std::string;
43 43
 
44 44
 #define SIP_FLAGS_NOTAG        1<<3 // don't add to-tag in reply
45 45
 
46
+#define SIP_FLAGS_NOBL         1<<4 // do not use destination blacklist
47
+
46 48
 /** \brief SIP transaction representation */
47 49
 struct AmSipTransaction
48 50
 {
... ...
@@ -416,6 +416,9 @@ bool AmSipDialog::onRxReplyStatus(const AmSipReply& reply,
416 416
       }
417 417
       break;
418 418
 
419
+    //case Connected: // late 200...
420
+    //  TODO: if reply.to_tag != getRemoteTag()
421
+    //        -> ACK + BYE (+absorb answer)
419 422
     default:
420 423
       break;
421 424
     }
... ...
@@ -81,6 +81,13 @@ int _SipCtrlInterface::load()
81 81
 	DBG("accept_fr_without_totag = %s\n", 
82 82
 	    trans_layer::accept_fr_without_totag?"yes":"no");
83 83
 
84
+	if (cfg.hasParameter("default_bl_ttl")) {
85
+	    trans_layer::default_bl_ttl = 
86
+		cfg.getParameterInt("default_bl_ttl",
87
+				    trans_layer::default_bl_ttl);
88
+	}
89
+	DBG("default_bl_ttl = %u\n",trans_layer::default_bl_ttl);
90
+
84 91
 	if (cfg.hasParameter("log_raw_messages")) {
85 92
 	    string msglog = cfg.getParameter("log_raw_messages");
86 93
 	    if (msglog == "no") trsp_socket::log_level_raw_msgs = -1;
... ...
@@ -132,7 +132,7 @@ enum sip_timer_type {
132 132
 // Blacklist grace timer (client transaction only)
133 133
 // - set after locally generated 408
134 134
 //   to wait for downstream 408
135
-#define DEFAULT_BL_TIMER T4_TIMER
135
+#define DEFAULT_BL_TIMER DEFAULT_B_TIMER
136 136
 
137 137
 #define A_TIMER sip_timers[STIMER_A]
138 138
 #define B_TIMER sip_timers[STIMER_B]
... ...
@@ -154,7 +154,6 @@ enum sip_timer_type {
154 154
 
155 155
 #define BL_TIMER sip_timers[STIMER_BL]
156 156
 
157
-//#define n_sip_timers 'M'-'A'+1
158 157
 extern unsigned int sip_timers[__STIMER_MAX];
159 158
 
160 159
 #define T2_TIMER sip_timer_t2
... ...
@@ -162,6 +162,7 @@ const char* _state_name_lookup[] = {
162 162
     "CONFIRMED",
163 163
     "TERMINATED_200",
164 164
     "TERMINATED",
165
+    "ABANDONED",
165 166
     "REMOVED"
166 167
 };
167 168
 
... ...
@@ -62,6 +62,7 @@ enum {
62 62
     TS_TERMINATED_200,
63 63
     TS_TERMINATED, // UAC:INV,!INV; UAS:INV,!INV
64 64
 
65
+    TS_ABANDONED,
65 66
     TS_REMOVED
66 67
 };
67 68
 
... ...
@@ -77,6 +78,8 @@ class sip_trans;
77 78
 class trans_timer
78 79
     : protected timer
79 80
 {
81
+    trans_timer(const trans_timer& ti) {}
82
+
80 83
 public:
81 84
     unsigned int type;
82 85
     unsigned int bucket_id;
... ...
@@ -88,6 +91,11 @@ public:
88 91
 	  bucket_id(bucket_id), t(t)
89 92
     {}
90 93
 
94
+    trans_timer(const trans_timer& ti, int bucket_id, sip_trans* t)
95
+        : timer(ti.expires), type(ti.type),
96
+	  bucket_id(bucket_id), t(t)
97
+    {}
98
+
91 99
     void fire();
92 100
 };
93 101
 
... ...
@@ -95,14 +103,6 @@ class sip_trans
95 103
 {
96 104
     trans_timer* timers[SIP_TRANS_TIMERS];
97 105
 
98
-    /**
99
-     * Resets a specific timer
100
-     *
101
-     * @param t the new timer
102
-     * @param timer_type @see sip_timer_type
103
-    */
104
-    void reset_timer(trans_timer* t, unsigned int timer_type);
105
-
106 106
  public:
107 107
     /** Transaction type */
108 108
     unsigned int type;
... ...
@@ -161,7 +161,6 @@ class sip_trans
161 161
      * @param timer_type @see sip_timer_type
162 162
      */
163 163
     trans_timer* get_timer(unsigned int timer_type);
164
-
165 164
     
166 165
     /**
167 166
      * Resets a specfic timer with a delay value
... ...
@@ -174,6 +173,14 @@ class sip_trans
174 173
 		     unsigned int expire_delay /* ms */,
175 174
 		     unsigned int bucket_id);
176 175
 
176
+    /**
177
+     * Resets a specific timer
178
+     *
179
+     * @param t the new timer
180
+     * @param timer_type @see sip_timer_type
181
+    */
182
+    void reset_timer(trans_timer* t, unsigned int timer_type);
183
+
177 184
     /**
178 185
      * Clears a specfic timer
179 186
      *
180 187
new file mode 100644
... ...
@@ -0,0 +1,126 @@
1
+#include "tr_blacklist.h"
2
+#include <string.h>
3
+
4
+#include "hash.h"
5
+
6
+#define BLACKLIST_HT_POWER 6
7
+#define BLACKLIST_HT_SIZE  (1 << BLACKLIST_HT_POWER)
8
+#define BLACKLIST_HT_MASK  (BLACKLIST_HT_SIZE - 1)
9
+
10
+#define DBG_BL INFO
11
+
12
+bl_addr::bl_addr()
13
+{
14
+  ss_family = AF_INET;
15
+}
16
+
17
+bl_addr::bl_addr(const bl_addr& addr)
18
+{
19
+  memcpy(this,&addr,SA_len(&addr));
20
+}
21
+
22
+bl_addr::bl_addr(const sockaddr_storage* p_addr)
23
+{
24
+  memcpy((sockaddr_storage*)this,p_addr,SA_len(p_addr));
25
+}
26
+
27
+unsigned int bl_addr::hash()
28
+{
29
+  return hashlittle((sockaddr_storage*)this, SA_len(this), 0)
30
+    & BLACKLIST_HT_MASK;
31
+}
32
+
33
+bool bl_addr_less::operator() (const bl_addr& l, const bl_addr& r) const
34
+{
35
+  if(l.ss_family != r.ss_family)
36
+    return l.ss_family < r.ss_family;
37
+
38
+  return memcmp(&l,&r,SA_len(&l));
39
+}
40
+
41
+void bl_timer::fire()
42
+{
43
+  DBG_BL("blacklist: %s/%i expired",
44
+	 am_inet_ntop(&addr).c_str(),am_get_port(&addr));
45
+  tr_blacklist::instance()->remove(&addr);
46
+}
47
+
48
+bool blacklist_bucket::insert(const bl_addr& addr, unsigned int duration /* ms */,
49
+			      const char* reason)
50
+{
51
+  wheeltimer* wt = wheeltimer::instance();
52
+  unsigned int expires = duration / (TIMER_RESOLUTION/1000);
53
+  expires += wt->wall_clock;
54
+
55
+  bl_timer* t = new bl_timer(addr,expires);
56
+  bl_entry* bl_e = new bl_entry(t);
57
+
58
+  if(!bl_bucket_base::insert(addr,bl_e)) {
59
+    delete t;
60
+    return false;
61
+  }
62
+
63
+  DBG_BL("blacklist: added %s/%i (%s/TTL=%.1fs)",
64
+	 am_inet_ntop(&addr).c_str(),am_get_port(&addr),
65
+	 reason,(float)duration/1000.0);
66
+
67
+  wt->insert_timer(t);
68
+  return true;
69
+}
70
+
71
+bool blacklist_bucket::remove(const bl_addr& addr)
72
+{
73
+  value_map::iterator it = find(addr);
74
+
75
+  if(it != elmts.end()){
76
+    bl_entry* v = it->second;
77
+    wheeltimer::instance()->remove_timer(v->t);
78
+    elmts.erase(it);
79
+    allocator().dispose(v);
80
+    return true;
81
+  }
82
+
83
+  return false;
84
+}
85
+
86
+_tr_blacklist::_tr_blacklist()
87
+  : blacklist_ht(BLACKLIST_HT_SIZE)
88
+{
89
+}
90
+
91
+_tr_blacklist::~_tr_blacklist()
92
+{ 
93
+}
94
+
95
+bool _tr_blacklist::exist(const sockaddr_storage* addr)
96
+{
97
+  bool res;
98
+  blacklist_bucket* bucket = get_bucket(hashlittle(addr, SA_len(addr), 0)
99
+					& BLACKLIST_HT_MASK);
100
+  bucket->lock();
101
+  res = bucket->exist(*(const bl_addr*)addr);
102
+  bucket->unlock();
103
+
104
+  return res;
105
+}
106
+
107
+void _tr_blacklist::insert(const sockaddr_storage* addr, unsigned int duration,
108
+			   const char* reason)
109
+{
110
+  blacklist_bucket* bucket = get_bucket(hashlittle(addr, SA_len(addr), 0)
111
+					& BLACKLIST_HT_MASK);
112
+  bucket->lock();
113
+  if(!bucket->exist(*(const bl_addr*)addr)) {
114
+    bucket->insert(*(const bl_addr*)addr,duration,reason);
115
+  }
116
+  bucket->unlock();
117
+}
118
+
119
+void _tr_blacklist::remove(const sockaddr_storage* addr)
120
+{
121
+  blacklist_bucket* bucket = get_bucket(hashlittle(addr, SA_len(addr), 0)
122
+					& BLACKLIST_HT_MASK);
123
+  bucket->lock();
124
+  bucket->remove(*(const bl_addr*)addr);
125
+  bucket->unlock();
126
+}
0 127
new file mode 100644
... ...
@@ -0,0 +1,97 @@
1
+#ifndef _tr_blacklist_h_
2
+#define _tr_blacklist_h_
3
+
4
+#include "hash_table.h"
5
+#include "singleton.h"
6
+
7
+#include "ip_util.h"
8
+#include "wheeltimer.h"
9
+
10
+/**
11
+ * Blacklist bucket: key type
12
+ */
13
+struct bl_addr: public sockaddr_storage
14
+{
15
+  bl_addr();
16
+  bl_addr(const bl_addr&);
17
+  bl_addr(const sockaddr_storage*);
18
+
19
+  unsigned int hash();
20
+};
21
+
22
+struct bl_addr_less
23
+{
24
+  bool operator() (const bl_addr& l, const bl_addr& r) const;
25
+};
26
+
27
+struct bl_entry;
28
+
29
+typedef ht_map_bucket<bl_addr,bl_entry,
30
+		      ht_delete<bl_entry>,
31
+		      bl_addr_less> bl_bucket_base;
32
+
33
+class blacklist_bucket
34
+  : public bl_bucket_base
35
+{
36
+public:
37
+  blacklist_bucket(unsigned long id)
38
+  : bl_bucket_base(id)
39
+  {}
40
+
41
+  bool insert(const bl_addr& addr, unsigned int duration /* ms */,
42
+	      const char* reason);
43
+  bool remove(const bl_addr& addr);
44
+};
45
+
46
+typedef blacklist_bucket::value_map::iterator blacklist_elmt;
47
+
48
+struct bl_timer
49
+  : public timer
50
+{
51
+  bl_addr addr;
52
+
53
+  bl_timer()
54
+    : timer(), addr()
55
+  {}
56
+
57
+  bl_timer(const bl_addr& addr, unsigned int expires)
58
+    : timer(expires), addr(addr)
59
+  {}
60
+
61
+  void fire();
62
+};
63
+
64
+/**
65
+ * Blacklist bucket: value type
66
+ */
67
+struct bl_entry
68
+{
69
+  bl_timer* t;
70
+
71
+  bl_entry() {}
72
+
73
+  bl_entry(bl_timer* t)
74
+    : t(t)
75
+  {}
76
+};
77
+
78
+typedef hash_table<blacklist_bucket> blacklist_ht;
79
+
80
+class _tr_blacklist
81
+  : protected blacklist_ht
82
+{
83
+protected:
84
+  _tr_blacklist();
85
+  ~_tr_blacklist();
86
+
87
+public:
88
+  // public blacklist API:
89
+  bool exist(const sockaddr_storage* addr);
90
+  void insert(const sockaddr_storage* addr, unsigned int duration /* ms */,
91
+	      const char* reason);
92
+  void remove(const sockaddr_storage* addr);
93
+};
94
+
95
+typedef singleton<_tr_blacklist> tr_blacklist;
96
+
97
+#endif
... ...
@@ -47,6 +47,9 @@
47 47
 
48 48
 #include "wheeltimer.h"
49 49
 #include "sip_timers.h"
50
+#include "tr_blacklist.h"
51
+
52
+#define DEFAULT_BL_TTL 60000 /* 60s */
50 53
 
51 54
 #include "log.h"
52 55
 
... ...
@@ -64,6 +67,7 @@
64 67
 #include <algorithm>
65 68
 
66 69
 bool _trans_layer::accept_fr_without_totag = false;
70
+unsigned int _trans_layer::default_bl_ttl = DEFAULT_BL_TTL;
67 71
 
68 72
 _trans_layer::_trans_layer()
69 73
     : ua(NULL),
... ...
@@ -771,7 +775,8 @@ int _trans_layer::set_next_hop(sip_msg* msg,
771 775
 }
772 776
 
773 777
 
774
-int _trans_layer::set_destination_ip(sip_msg* msg, cstring* next_hop, unsigned short next_port)
778
+int _trans_layer::set_destination_ip(sip_msg* msg, cstring* next_hop,
779
+				     unsigned short next_port)
775 780
 {
776 781
 
777 782
     string nh = c2stlstr(*next_hop);
... ...
@@ -873,7 +878,14 @@ void _trans_layer::timeout(trans_bucket* bucket, sip_trans* t)
873 878
     translate_hdr(&msg,msg.callid, req,req->callid);
874 879
 
875 880
     string dialog_id(t->dialog_id.s,t->dialog_id.len);
876
-    bucket->remove(t);
881
+
882
+    if(t->flags & TR_FLAG_DISABLE_BL) {
883
+	bucket->remove(t);
884
+    }
885
+    else {
886
+	// set blacklist timer
887
+	t->reset_timer(STIMER_BL,BL_TIMER,bucket->get_id());
888
+    }
877 889
     bucket->unlock();
878 890
 
879 891
     ua->handle_sip_reply(dialog_id,&msg);
... ...
@@ -1048,7 +1060,8 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt,
1048 1060
 	    inc_ref(e);
1049 1061
 	    //TODO: avoid to loose the transport from the next-hop-list
1050 1062
 	    e->next_ip(&msg->h_dns,&msg->remote_ip);
1051
-	    DBG("destination set to <%s>\n",get_addr_str(&msg->remote_ip).c_str());
1063
+	    DBG("destination set to <%s>\n",
1064
+		get_addr_str(&msg->remote_ip).c_str());
1052 1065
 	}
1053 1066
     }
1054 1067
     else {
... ...
@@ -1069,6 +1082,54 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt,
1069 1082
 	}
1070 1083
     }
1071 1084
 
1085
+    if(tr_blacklist::instance()->exist(&msg->remote_ip)) {
1086
+
1087
+	sockaddr_storage sa;
1088
+	do {
1089
+	    memset(&sa,0,sizeof(sockaddr_storage));
1090
+
1091
+	    // get the next ip
1092
+	    if(msg->h_dns.next_ip(&sa) < 0){
1093
+		DBG("next_ip(): no more destinations! reply 500");
1094
+		sip_msg err;
1095
+		err.type = SIP_REPLY;
1096
+		err.u.reply = new sip_reply();
1097
+		err.u.reply->code = 500;
1098
+		err.u.reply->reason = cstring("All destinations blacklisted");
1099
+
1100
+		// pre-parse error message
1101
+		err.from = msg->from;
1102
+		err.from->p = new sip_from_to();
1103
+		parse_from_to((sip_from_to*)err.from->p,
1104
+			      err.from->value.s,err.from->value.len);
1105
+
1106
+		err.to = msg->to;
1107
+		err.to->p = new sip_from_to();
1108
+		parse_from_to((sip_from_to*)err.to->p,
1109
+			      err.to->value.s,err.to->value.len);
1110
+
1111
+		err.cseq = msg->cseq;
1112
+		err.cseq->p = new sip_cseq();
1113
+		parse_cseq((sip_cseq*)err.cseq->p,
1114
+			   err.cseq->value.s,err.cseq->value.len);
1115
+
1116
+		err.callid = msg->callid;
1117
+
1118
+		ua->handle_sip_reply(c2stlstr(dialog_id),&err);
1119
+		return 0;
1120
+	    }
1121
+    
1122
+	    //If a SRV record is involved, the port number
1123
+	    // should have been set by h_dns.next_ip(...).
1124
+	    if(!am_get_port(&sa)){
1125
+		//Else, we copy the old port number
1126
+		am_set_port(&sa,am_get_port(&msg->remote_ip));
1127
+	    }
1128
+	} while(tr_blacklist::instance()->exist(&sa));
1129
+	
1130
+	memcpy(&msg->remote_ip,&sa,sizeof(sockaddr_storage));
1131
+    }
1132
+
1072 1133
     // rco: should we overwrite the socket from the request in all cases???
1073 1134
     if((out_interface >= 0) && ((unsigned int)out_interface < transports.size())){
1074 1135
 	if(msg->local_socket) dec_ref(msg->local_socket);
... ...
@@ -1102,7 +1163,7 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt,
1102 1163
     }
1103 1164
 
1104 1165
     string ruri; // buffer needs to be @ function scope
1105
-    if((flags & SEND_REQUEST_FLAG_NEXT_HOP_RURI) &&
1166
+    if((flags & TR_FLAG_NEXT_HOP_RURI) &&
1106 1167
        (patch_ruri_with_remote_ip(ruri,msg) < 0)) {
1107 1168
  	return -1;
1108 1169
     }
... ...
@@ -1225,6 +1286,7 @@ int _trans_layer::cancel(trans_ticket* tt, const cstring& hdrs)
1225 1286
 	return -1;
1226 1287
 	
1227 1288
     case TS_PROCEEDING:
1289
+    case TS_ABANDONED:
1228 1290
 	// continue with CANCEL request
1229 1291
 	break;
1230 1292
 
... ...
@@ -1541,7 +1603,7 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1541 1603
     cstring to_tag;
1542 1604
     int     reply_code = msg->u.reply->code;
1543 1605
 
1544
-    DBG("update_uac_reply(reply code = %i, trans=%p)\n",msg->u.reply->code, t);
1606
+    DBG("update_uac_reply(reply code = %i, trans=%p)\n",reply_code, t);
1545 1607
 
1546 1608
     if(reply_code < 200){
1547 1609
 
... ...
@@ -1566,6 +1628,32 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1566 1628
 	    else
1567 1629
 		goto end;
1568 1630
 
1631
+	case TS_ABANDONED: // debug code
1632
+	    // disable blacklisting: remote UA did reply
1633
+	    INFO("disable blacklisting: remote UA (%s/%i) did reply",
1634
+		 am_inet_ntop(&msg->remote_ip).c_str(),
1635
+		 am_get_port(&msg->remote_ip));
1636
+	    t->flags |= TR_FLAG_DISABLE_BL;
1637
+	    bucket->unlock();
1638
+	    {
1639
+		// send CANCEL
1640
+		trans_ticket tt(t,bucket);
1641
+		cancel(&tt,cstring());
1642
+	    
1643
+		// Now remove the transaction
1644
+		bucket->lock();
1645
+		//bucket->remove(t);
1646
+	    }
1647
+	    goto end;
1648
+
1649
+	case TS_TERMINATED:
1650
+	    // disable blacklisting: remote UA did reply
1651
+	    INFO("disable blacklisting: remote UA (%s/%i) did reply",
1652
+		 am_inet_ntop(&msg->remote_ip).c_str(),
1653
+		 am_get_port(&msg->remote_ip));
1654
+	    t->flags |= TR_FLAG_DISABLE_BL;
1655
+	    goto end;
1656
+
1569 1657
 	case TS_COMPLETED:
1570 1658
 	default:
1571 1659
 	    goto end;
... ...
@@ -1589,7 +1677,9 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1589 1677
 	if(reply_code >= 300){
1590 1678
 	
1591 1679
 	    if(reply_code == 503) {
1592
-		if(!try_next_ip(bucket,t))
1680
+		tr_blacklist::instance()->insert(&t->msg->remote_ip,
1681
+						 default_bl_ttl,"503");
1682
+		if(!try_next_ip(bucket,t,false))
1593 1683
 		    goto end;
1594 1684
 	    }
1595 1685
     
... ...
@@ -1610,6 +1700,16 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1610 1700
 		
1611 1701
 		goto pass_reply;
1612 1702
 		
1703
+	    case TS_ABANDONED:
1704
+	    case TS_TERMINATED:
1705
+		// disable blacklisting: remote UA did reply
1706
+		INFO("disable blacklisting: remote UA (%s/%i) did reply",
1707
+		     am_inet_ntop(&msg->remote_ip).c_str(),
1708
+		     am_get_port(&msg->remote_ip));
1709
+
1710
+		t->flags |= TR_FLAG_DISABLE_BL;
1711
+		// fall through trap
1712
+
1613 1713
 	    case TS_COMPLETED:
1614 1714
 		// generate a new non-200 ACK
1615 1715
 		send_non_200_ack(msg,t);
... ...
@@ -1676,6 +1776,15 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1676 1776
 		t->retransmit();
1677 1777
 		goto end;
1678 1778
 
1779
+	    case TS_ABANDONED:
1780
+	    case TS_TERMINATED:
1781
+		//TODO: send ACK+BYE
1782
+		INFO("disable blacklisting: remote UA (%s/%i) did reply",
1783
+		     am_inet_ntop(&msg->remote_ip).c_str(),
1784
+		     am_get_port(&msg->remote_ip));
1785
+		t->flags |= TR_FLAG_DISABLE_BL;
1786
+		goto end;
1787
+
1679 1788
 	    default:
1680 1789
 		goto end;
1681 1790
 	    }
... ...
@@ -1684,7 +1793,9 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1684 1793
     else { // non-INVITE transaction
1685 1794
 
1686 1795
 	if(reply_code == 503) {
1687
-	    if(!try_next_ip(bucket,t))
1796
+	    tr_blacklist::instance()->insert(&t->msg->remote_ip,
1797
+					     default_bl_ttl,"503");
1798
+	    if(!try_next_ip(bucket,t,false))
1688 1799
 		goto end;
1689 1800
 	}
1690 1801
 
... ...
@@ -1711,6 +1822,14 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg*
1711 1822
 	    // Absorb reply retransmission (only if UDP)
1712 1823
 	    goto end;
1713 1824
 	    
1825
+	case TS_ABANDONED:
1826
+	case TS_TERMINATED:
1827
+	    INFO("disable blacklisting: remote UA (%s/%i) did reply",
1828
+		 am_inet_ntop(&msg->remote_ip).c_str(),
1829
+		 am_get_port(&msg->remote_ip));
1830
+	    t->flags |= TR_FLAG_DISABLE_BL;
1831
+	    goto end;
1832
+
1714 1833
 	default:
1715 1834
 	    goto end;
1716 1835
 	}
... ...
@@ -1981,6 +2100,15 @@ void _trans_layer::timer_expired(trans_timer* t, trans_bucket* bucket,
1981 2100
 	    timeout(bucket,tr);
1982 2101
 	    return;
1983 2102
 	}
2103
+	else if(tr->state == TS_ABANDONED) {
2104
+	    if(tr->flags & TR_FLAG_DISABLE_BL) {
2105
+		bucket->remove(tr);
2106
+	    }
2107
+	    else {
2108
+		// set blacklist timer
2109
+		tr->reset_timer(STIMER_BL,BL_TIMER,bucket->get_id());
2110
+	    }
2111
+	}
1984 2112
 	else {
1985 2113
 	    DBG("Transaction timeout timer hit while state=%s (0x%x)",
1986 2114
 		tr->state_str(), tr->state);
... ...
@@ -1993,8 +2121,8 @@ void _trans_layer::timer_expired(trans_timer* t, trans_bucket* bucket,
1993 2121
 	// Note: remember well, we first set timer C
1994 2122
 	//       after the first provisional reply.
1995 2123
 	tr->clear_timer(STIMER_C);
1996
-	if(tr->state != TS_PROCEEDING)
1997
-	    break; // shouldn't happen
2124
+	//if(tr->state != TS_PROCEEDING)
2125
+	//  break; // shouldn't happen
1998 2126
 
1999 2127
 	bucket->unlock();
2000 2128
 
... ...
@@ -2025,6 +2153,19 @@ void _trans_layer::timer_expired(trans_timer* t, trans_bucket* bucket,
2025 2153
 	    // unlocks the bucket
2026 2154
 	    timeout(bucket,tr);
2027 2155
 	    return;
2156
+	case TS_ABANDONED:
2157
+	    if(tr->flags & TR_FLAG_DISABLE_BL) {
2158
+		bucket->remove(tr);
2159
+	    }
2160
+	    else {
2161
+		// set blacklist timer
2162
+		tr->reset_timer(STIMER_BL,BL_TIMER,bucket->get_id());
2163
+	    }
2164
+	    break;
2165
+	default:
2166
+	    DBG("Transaction timeout timer hit while state=%s (0x%x)",
2167
+		tr->state_str(), tr->state);
2168
+	    bucket->remove(tr);
2028 2169
 	}
2029 2170
 	break;
2030 2171
 
... ...
@@ -2115,8 +2256,19 @@ void _trans_layer::timer_expired(trans_timer* t, trans_bucket* bucket,
2115 2256
 	}
2116 2257
     } break;
2117 2258
 
2118
-    case STIMER_M:
2119
-	try_next_ip(bucket,tr);
2259
+    case STIMER_M: {
2260
+	try_next_ip(bucket,tr,true);
2261
+    } break;
2262
+	
2263
+    case STIMER_BL:
2264
+	tr->clear_timer(STIMER_BL);
2265
+	if(!(tr->flags & TR_FLAG_DISABLE_BL)) {
2266
+	    // insert destination to blacklist
2267
+	    tr_blacklist::instance()->insert(&tr->msg->remote_ip,
2268
+					     default_bl_ttl,
2269
+					     "timeout");
2270
+	}
2271
+	bucket->remove(tr);
2120 2272
 	break;
2121 2273
 
2122 2274
     default:
... ...
@@ -2179,19 +2331,22 @@ trsp_socket* _trans_layer::find_transport(sockaddr_storage* remote_ip)
2179 2331
   if(tsock != NULL)
2180 2332
       return tsock;
2181 2333
 
2334
+  {
2182 2335
   // try with alternative address
2183
-  char local_ip[NI_MAXHOST];
2184
-  if(am_inet_ntop(&from,local_ip,NI_MAXHOST) != NULL) {
2185
-      map<string,unsigned short>::iterator if_it = AmConfig::LocalSIPIP2If.find(local_ip);
2336
+  string local_ip = am_inet_ntop(&from);
2337
+  if(!local_ip.empty()) {
2338
+      map<string,unsigned short>::iterator if_it =
2339
+	  AmConfig::LocalSIPIP2If.find(local_ip);
2186 2340
       if(if_it == AmConfig::LocalSIPIP2If.end()){
2187 2341
 	  ERROR("Could not find a local interface for "
2188 2342
 		"resolved local IP (local_ip='%s')",
2189
-		local_ip);
2343
+		local_ip.c_str());
2190 2344
       }
2191 2345
       else {
2192 2346
 	  tsock = transports[if_it->second];
2193 2347
       }
2194 2348
   }
2349
+  }
2195 2350
 
2196 2351
   return tsock;
2197 2352
 
... ...
@@ -2200,40 +2355,132 @@ trsp_socket* _trans_layer::find_transport(sockaddr_storage* remote_ip)
2200 2355
   return NULL;
2201 2356
 }
2202 2357
 
2203
-int _trans_layer::try_next_ip(trans_bucket* bucket, sip_trans* tr)
2358
+sip_trans* _trans_layer::copy_uac_trans(sip_trans* tr)
2204 2359
 {
2205
-    sockaddr_storage sa;
2206
-    memset(&sa,0,sizeof(sockaddr_storage));
2360
+    assert(tr && (tr->type == TT_UAC));
2361
+    sip_trans* n_tr = new sip_trans();
2207 2362
     
2208
-    // get the next ip
2209
-    if(tr->msg->h_dns.next_ip(&sa) < 0){
2210
-	tr->clear_timer(STIMER_M);
2211
-	return -1;
2363
+    n_tr->type  = tr->type;
2364
+    n_tr->flags = tr->flags;
2365
+
2366
+    if(tr->dialog_id.len) {
2367
+	n_tr->dialog_id.s = new char[tr->dialog_id.len];
2368
+	n_tr->dialog_id.len = tr->dialog_id.len;
2369
+	memcpy((void*)n_tr->dialog_id.s,tr->dialog_id.s,n_tr->dialog_id.len);
2212 2370
     }
2213
-    
2214
-    //If a SRV record is involved, the port number
2215
-    // should have been set by h_dns.next_ip(...).
2216
-    if(!((sockaddr_in*)&sa)->sin_port){
2217
-	//Else, we copy the old port number
2218
-	((sockaddr_in*)&sa)->sin_port = ((sockaddr_in*)&tr->msg->remote_ip)->sin_port;
2371
+
2372
+    if(tr->logger) {
2373
+	n_tr->logger = tr->logger;
2374
+	inc_ref(n_tr->logger);
2219 2375
     }
2376
+
2377
+    return n_tr;
2378
+}
2379
+
2380
+int _trans_layer::try_next_ip(trans_bucket* bucket, sip_trans* tr,
2381
+			      bool use_new_trans)
2382
+{
2383
+    tr->clear_timer(STIMER_M);
2384
+    sockaddr_storage sa;
2385
+    do {
2386
+	memset(&sa,0,sizeof(sockaddr_storage));
2220 2387
     
2221
-    // copy the new address back
2222
-    memcpy(&tr->msg->remote_ip,&sa,sizeof(sockaddr_storage));
2388
+	// get the next ip
2389
+	if(tr->msg->h_dns.next_ip(&sa) < 0){
2390
+	    DBG("no more destinations!");
2391
+	    return -1;
2392
+	}
2223 2393
     
2224
-    // create new branch tag
2225
-    compute_branch((char*)(tr->msg->via_p1->branch.s+MAGIC_BRANCH_LEN),
2226
-		   tr->msg->callid->value,tr->msg->cseq->value);
2394
+	//If a SRV record is involved, the port number
2395
+	// should have been set by h_dns.next_ip(...).
2396
+	if(!am_get_port(&sa)){
2397
+	    //Else, we copy the old port number
2398
+	    am_set_port(&sa,am_get_port(&tr->msg->remote_ip));
2399
+	}
2400
+    } while(tr_blacklist::instance()->exist(&sa));
2401
+
2227 2402
 
2228
-    if(tr->flags & SEND_REQUEST_FLAG_NEXT_HOP_RURI) {
2403
+    if(use_new_trans) {
2229 2404
 	string   n_uri;
2405
+	cstring  old_uri;
2406
+	auto_ptr<sip_trans> n_tr(copy_uac_trans(tr));
2407
+
2408
+	// Warning: no deep copy!!!
2409
+	//  -> do not forget to release() before it's too late!
2410
+	sip_msg tmp_msg(*tr->msg);
2411
+
2412
+	// remove last Via-HF
2413
+	tmp_msg.vias.pop_front();
2414
+
2415
+	// copy the new address back
2416
+	memcpy(&tmp_msg.remote_ip,&sa,sizeof(sockaddr_storage));
2417
+
2418
+	// backup R-URI before possible update
2419
+	old_uri = tr->msg->u.request->ruri_str;
2420
+
2421
+	if(n_tr->flags & TR_FLAG_NEXT_HOP_RURI) {
2422
+	    // patch R-URI, generate& parse new message
2423
+	    if(patch_ruri_with_remote_ip(n_uri, &tmp_msg)) {
2424
+		// TODO: error handling!
2425
+		ERROR("could not patch R-URI with new destination");
2426
+		tmp_msg.release();
2427
+		return -1;
2428
+	    }
2429
+	}
2430
+
2230 2431
 	sip_msg* p_msg=NULL;
2231
-	if(!patch_ruri_with_remote_ip(n_uri, tr->msg) &&
2232
-	   !generate_and_parse_new_msg(tr->msg,p_msg)) {
2432
+	if(generate_and_parse_new_msg(&tmp_msg,p_msg)) {
2433
+	    // TODO: error handling!
2434
+	    ERROR("could not generate&parse new message");
2435
+	    tmp_msg.release();
2436
+	    return -1;
2437
+	}
2438
+
2439
+	tmp_msg.release();
2440
+	n_tr->msg = p_msg;
2441
+
2442
+	// Abandon old transaction
2443
+	tr->clear_timer(STIMER_A);
2444
+	tr->state = TS_ABANDONED;
2445
+
2446
+	// restore old R-URI
2447
+	tr->msg->u.request->ruri_str = old_uri;
2448
+
2449
+	trans_timer* t_bf = tr->get_timer(STIMER_B);
2450
+	tr = n_tr.release();
2451
+
2452
+	// keep old timer B/F
2453
+	if(t_bf) {
2454
+	    t_bf = new trans_timer(*t_bf,bucket->get_id(),tr);
2455
+	    tr->reset_timer(t_bf,t_bf->type);
2456
+	}
2457
+
2458
+	bucket->append(tr);
2459
+	
2460
+    }
2461
+    else {
2462
+	// copy the new address back
2463
+	memcpy(&tr->msg->remote_ip,&sa,sizeof(sockaddr_storage));
2464
+
2465
+	if(tr->flags & TR_FLAG_NEXT_HOP_RURI) {
2466
+	    string   n_uri;
2467
+	    sip_msg* p_msg=NULL;
2468
+
2469
+	    // patch R-URI, generate& parse new message
2470
+	    if(patch_ruri_with_remote_ip(n_uri, tr->msg) ||
2471
+	       generate_and_parse_new_msg(tr->msg,p_msg)) {
2472
+		ERROR("could not patch R-URI with new destination");
2473
+		return -1;
2474
+	    }
2233 2475
 
2234 2476
 	    delete tr->msg;
2235 2477
 	    tr->msg = p_msg;
2236
-	}		
2478
+	}
2479
+	else {
2480
+	    // only create new branch tag
2481
+	    compute_branch((char*)(tr->msg->via_p1->branch.s+MAGIC_BRANCH_LEN),
2482
+			   tr->msg->callid->value,tr->msg->cseq->value);
2483
+	}
2237 2484
     }
2238 2485
    
2239 2486
     stats.inc_sent_requests();
... ...
@@ -2255,26 +2502,21 @@ int _trans_layer::try_next_ip(trans_bucket* bucket, sip_trans* tr)
2255 2502
 	    tr->reset_timer(STIMER_A,A_TIMER,bucket->get_id());
2256 2503
 	}
2257 2504
 	if(!tr->get_timer(STIMER_B)) {
2258
-	    // Timer B has been cleared by 1xx
2259 2505
 	    tr->reset_timer(STIMER_B,B_TIMER,bucket->get_id());
2260 2506
 	}
2261 2507
     }
2262 2508
     else {
2263
-	// no need to reset state here:
2264
-	// transaction layer does not make a difference
2265
-	// between TS_TRYING or TS_PROCEEDING on timeout
2509
+	tr->state = TS_TRYING;
2266 2510
 	if(!tr->msg->local_socket->is_reliable()) {
2267 2511
 	    tr->reset_timer(STIMER_E,E_TIMER,bucket->get_id());
2268 2512
 	}
2269
-	// Timer F is not cleared by 1xx,
2270
-	// as provisional replies should not be sent
2271
-	// for non-INVITE transactions
2513
+	if(!tr->get_timer(STIMER_F)) {
2514
+	    tr->reset_timer(STIMER_F,F_TIMER,bucket->get_id());
2515
+	}
2272 2516
     }
2273 2517
     
2274 2518
     if(!tr->msg->h_dns.eoip())
2275 2519
 	tr->reset_timer(STIMER_M,M_TIMER,bucket->get_id());
2276
-    else
2277
-	tr->clear_timer(STIMER_M);
2278 2520
 
2279 2521
     return 0;
2280 2522
 }
... ...
@@ -55,7 +55,9 @@ class sip_ua;
55 55
 class msg_logger;
56 56
 
57 57
 // replace the RURI-host with next-hop IP / port
58
-#define SEND_REQUEST_FLAG_NEXT_HOP_RURI 1
58
+#define TR_FLAG_NEXT_HOP_RURI 1
59
+// disable blacklist
60
+#define TR_FLAG_DISABLE_BL    2
59 61
 
60 62
 /* Each counter has a method for incrementing to allow changing implementation
61 63
  * of the stats class later without touching the code using it. (One possible
... ...
@@ -118,6 +120,11 @@ public:
118 120
      */
119 121
     static bool accept_fr_without_totag;
120 122
 
123
+    /**
124
+     * Config option: default blacklist time-to-live
125
+     */
126
+    static unsigned int default_bl_ttl;
127
+
121 128
     /**
122 129
      * Register a SIP UA.
123 130
      * This method MUST be called ONCE.
... ...
@@ -237,13 +244,15 @@ protected:
237 244
      */
238 245
     int set_destination_ip(sip_msg* msg, cstring* next_hop, unsigned short next_port);    
239 246
 
247
+    sip_trans* copy_uac_trans(sip_trans* tr);
248
+
240 249
     /**
241 250
      * If the destination has multiple IPs (SRV records),
242 251
      * try the next destination IP.
243 252
      * @return 0 if the message has been re-sent.
244 253
      *        -1 if no additional destination has been found.
245 254
      */
246
-    int try_next_ip(trans_bucket* bucket, sip_trans* tr);
255
+    int try_next_ip(trans_bucket* bucket, sip_trans* tr, bool use_new_trans);
247 256
 
248 257
     /**
249 258
      * Implements the state changes for the UAC state machine