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.
... | ... |
@@ -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); |
... | ... |
@@ -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 |
... | ... |
@@ -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 |