Hi guys,
I'd like to propose another possibility for a highly-available and
scalable system design based on SER:
The problems I've encountered for scalable systems are:
- Distribution of the user location and alias location among the nodes
(user location is based on registrations, alias location comes from
web interfaces and is used for call forwarding).
- Reloading up to date location tables after breakdown and recovery of a
node
So I'm just thinking loud about the following provisioning system:
- Write a client which fulfills the this demands:
- Receive one or more locations from SER via a SER module or from a
web application and distribute them to other
known clients. Take care of retransmissions if a client isn't
reachable or reports a temporary failure.
- Receive one or more locations from other clients and write
them into the SER FIFO. If writing into the FIFO fails, try to
write directly into the database (location-table, alias-table etc.).
Report a temporary failure if this also fails.
Maybe a centralized server should be used which receives the locations
from the clients and distributes them to other clients, so that the
nodes just know about the server and nothing about other nodes. This
would make integration of new nodes easy.
On the other hand, it's another single point of failure, so a
decentralized solution should be considered. But that would mean that
you've to inform every node about the existence of a new node.
The protocol used between the nodes should be simple and fast. So I
think SOAP drops out here. Maybe XMLRPC or ICE
(http://www.zeroc.com/ice.html) could be used.
One might think now why not just use replication on SIP layer, but
t_replicate only supports one peer and you've no possibility to get
locations on a node while it's down. Replication of other location
tables like the alias-table is also not possible.
I'd be willing to release these parts as GPL for creating an open
framework for carrier-grade SER integration, so any feedback,
improvements or flames are highly welcome.
Cheers,
Andy
I am relatively new to SER. My SIP service provider requires me to
present the ANI in the "From" field in my INVITE message header in E.164
format. I am currently sending this information as follows:
From: "2125551212" <sip:2125551212@xxx.xxx.xxx.xxx>
They want it in E.164 format as follows:
From: "+12125551212" <sip:+12125551212@xxx.xxx.xxx.xxx>
I see function only to modify the SIP URI but not the other header or
SDP fields.
Could someone help me with a code snippet that I can use in ser.cfg to
do this conversion?
Regards,
SCM
Does anyone know if there is a way to authenticate SER to MS Active
Directory? I'd like to use an existing user database instead of
having to manage users myself, and most of our users are in Active
Directory.
Has anyone tried this?
What are most SER users using for authentication? Is anyone here
tying into any kind of existing organization user database?
We are using SER with the PostgreSQL module, so that SER and our
Asterisk PSTN gateway and voice mail system can use the same back-end
database for user records. (Anyone who's interested in how we did this,
let me know -- it's still a little shaky, but it pretty much works.)
Recently we have encountered a problem: it seems that if any database
error occurs, SER hangs or crashes. We have so far been able to mostly
work around these conditions by being Really Careful with the database,
but it is a little unsettling.
The database schema we're working from is practically identical to the
MySQL schema that ships with SER. Notably for this problem the lengths
of all CHARACTER VARYING fields are the same. The User-Agent field is
CHARACTER VARYING(50), for instance.
So ... today boss got a copy of Xten's "eyeBeam" software, set it up
to register to our SER ... and SER promptly locked up. I looked in the
PostgreSQL database log and found this:
Feb 10 13:39:32 mabell postgres[13058]: [267] ERROR: value too long for
type character varying(50)
Whoops. SER tried to insert eyeBeam's User-Agent data, PostgreSQL
returned a data integrity error, and SER went bye-bye.
So I did a little ALTER TABLE and adjusted the field to length 128 ...
then put this in ser.cfg:
# Disallow long user-agent
if (search("^User-Agent: .{100,}") ) {
log(1, "LOG: User agent too long\n");
sl_send_reply("479", "User agent too long!");
break;
};
But this is obviously a kludge ... and doesn't fix the problem of one of
the *other* fields being over-long.
Anyone have any thoughts on this? Robustness patches for the PostgreSQL
module or usrloc? Better ser.cfg workarounds to protect the world from
malformed or over-long SIP header fields? Should I just change all the
database fields to TEXT instead of CHARACTER VARYING(whatever) ?
--
Karl A. Krueger <kkrueger(a)whoi.edu>
Network Security -- Linux/Unix Systems Support -- Etc.
Woods Hole Oceanographic Institution
Hi,
I´ve configured freeradius and SER according to the Radius HOW TO
document, Accounting works very well but now I am doing some tests
trying to do user authentication however all the authentication
requests coming to the freeradius fails and X-lite sipphone is
receiving an Unauthorized message from SER, please some advice,
thanks
rafael
PS: config files...
in /usr/local/etc/raddb/users :
---------
test Auth-Type := Digest, User-Password == "test"
Reply-Message = "Hello, test with digest"
6609876 Auth-Type := Digest
User-Password := "9876",
Digest-Response = "lalalalala",
Reply-Message = "Hello, ibm1"
6604321 Auth-Type := Digest
User-Password := "4321",
Digest-Response = "lalalalala",
Reply-Message = "Hello, ibm2"
---------
Some relevant data in ser.cfg:
...
modparam("group_radius", "use_domain", 0)
....
if (uri==myself) {
if (method=="REGISTER") {
# Uncomment this if you want to use digest authentication
if (!radius_www_authorize("")) {
www_challenge("", "1");
break;
};
if (!save("location")) {
sl_reply_error();
};
break;
};
lookup("aliases");
if (!uri==myself) {
append_hf("P-hint: outbound alias\r\n");
route(1);
break;
};
# does the user wish redirection on no availability?
(i.e., is he
# in the voicemail group?) -- determine it now and store it in
# flag 4, before we rewrite the flag using UsrLoc
if (radius_is_user_in("Request-URI", "voicemail")) {
log(1, "requested user is in voicemail group");
setflag(4);
};
# native SIP destinations are handled using our USRLOC DB
if (!lookup("location")) {
# sl_send_reply("404", "Not Found");
log(1,"unable to locate user");
route(4);
break;
};
}; # End of "if(uri==myself)"
....
------------------RADIUSD -X Output ---------------------------:
rad_recv: Access-Request packet from host 127.0.0.1:33187, id=79, length=311
User-Name = "6604321(a)10.0.1.22"
Digest-Attributes = 0x0a0936363034333231
Digest-Attributes = 0x010b31302e302e312e3232
Digest-Attributes =
0x022a34323366333163373062336631643261643330383833633238303434316632663133643136613830
Digest-Attributes = 0x040f7369703a31302e302e312e3232
Digest-Attributes = 0x030a5245474953544552
Digest-Attributes = 0x050661757468
Digest-Attributes = 0x090a3030303030303162
Digest-Attributes =
0x08224433343132424232394131453131443939334232303035304241373836433642
Digest-Response = "a6a7812ac0331324f977453c228da2ed"
Service-Type = IAPP-Register
Sip-URI-User = "6604321"
Cisco-AVPair = "call-id=D3412ADB9A1E11D993B20050BA786C6B(a)10.0.1.22"
NAS-IP-Address = 127.0.0.1
NAS-Port = 5060
Processing the authorize section of radiusd.conf
modcall: entering group authorize for request 8
modcall[authorize]: module "preprocess" returns ok for request 8
modcall[authorize]: module "chap" returns noop for request 8
modcall[authorize]: module "mschap" returns noop for request 8
rlm_digest: Converting Digest-Attributes to something sane...
Digest-User-Name = "6604321"
Digest-Realm = "10.0.1.22"
Digest-Nonce = "423f31c70b3f1d2ad30883c280441f2f13d16a80"
Digest-URI = "sip:10.0.1.22"
Digest-Method = "REGISTER"
Digest-QOP = "auth"
Digest-Nonce-Count = "0000001b"
Digest-CNonce = "D3412BB29A1E11D993B20050BA786C6B"
rlm_digest: Adding Auth-Type = DIGEST
modcall[authorize]: module "digest" returns ok for request 8
rlm_realm: Looking up realm "10.0.1.22" for User-Name = "6604321(a)10.0.1.22"
rlm_realm: No such realm "10.0.1.22"
modcall[authorize]: module "suffix" returns noop for request 8
rlm_eap: No EAP-Message, not doing EAP
modcall[authorize]: module "eap" returns noop for request 8
users: Matched DEFAULT at 152
modcall[authorize]: module "files" returns ok for request 8
modcall: group authorize returns ok for request 8
rad_check_password: Found Auth-Type DIGEST
auth: type "digest"
Processing the authenticate section of radiusd.conf
modcall: entering group authenticate for request 8
rlm_digest: Configuration item "User-Password" is required for authentication.
modcall[authenticate]: module "digest" returns invalid for request 8
modcall: group authenticate returns invalid for request 8
auth: Failed to validate the user.
Delaying request 8 for 1 seconds
Finished request 8
Going to the next request
--- Walking the entire request list ---
Waking up in 1 seconds...
rad_recv: Access-Request packet from host 127.0.0.1:33188, id=80, length=311
User-Name = "6609876(a)10.0.1.22"
Digest-Attributes = 0x0a0936363039383736
Digest-Attributes = 0x010b31302e302e312e3232
Digest-Attributes =
0x022a34323366333163373062336631643261643330383833633238303434316632663133643136613830
Digest-Attributes = 0x040f7369703a31302e302e312e3232
Digest-Attributes = 0x030a5245474953544552
Digest-Attributes = 0x050661757468
Digest-Attributes = 0x090a3030303030303163
Digest-Attributes =
0x08224433343132424235394131453131443939334232303035304241373836433642
Digest-Response = "50fa695654b20e2eec54a1003fe15d9f"
Service-Type = IAPP-Register
Sip-URI-User = "6609876"
Cisco-AVPair = "call-id=D3412ADE9A1E11D993B20050BA786C6B(a)10.0.1.22"
NAS-IP-Address = 127.0.0.1
NAS-Port = 5060
Processing the authorize section of radiusd.conf
modcall: entering group authorize for request 9
modcall[authorize]: module "preprocess" returns ok for request 9
modcall[authorize]: module "chap" returns noop for request 9
modcall[authorize]: module "mschap" returns noop for request 9
rlm_digest: Converting Digest-Attributes to something sane...
Digest-User-Name = "6609876"
Digest-Realm = "10.0.1.22"
Digest-Nonce = "423f31c70b3f1d2ad30883c280441f2f13d16a80"
Digest-URI = "sip:10.0.1.22"
Digest-Method = "REGISTER"
Digest-QOP = "auth"
Digest-Nonce-Count = "0000001c"
Digest-CNonce = "D3412BB59A1E11D993B20050BA786C6B"
rlm_digest: Adding Auth-Type = DIGEST
modcall[authorize]: module "digest" returns ok for request 9
rlm_realm: Looking up realm "10.0.1.22" for User-Name = "6609876(a)10.0.1.22"
rlm_realm: No such realm "10.0.1.22"
modcall[authorize]: module "suffix" returns noop for request 9
rlm_eap: No EAP-Message, not doing EAP
modcall[authorize]: module "eap" returns noop for request 9
users: Matched DEFAULT at 152
modcall[authorize]: module "files" returns ok for request 9
modcall: group authorize returns ok for request 9
rad_check_password: Found Auth-Type DIGEST
auth: type "digest"
Processing the authenticate section of radiusd.conf
modcall: entering group authenticate for request 9
rlm_digest: Configuration item "User-Password" is required for authentication.
modcall[authenticate]: module "digest" returns invalid for request 9
modcall: group authenticate returns invalid for request 9
auth: Failed to validate the user.
Delaying request 9 for 1 seconds
Finished request 9
Going to the next request
--- Walking the entire request list ---
Waking up in 1 seconds...
--- Walking the entire request list ---
Sending Access-Reject of id 79 to 127.0.0.1:33187
Waking up in 1 seconds...
rad_recv: Access-Request packet from host 127.0.0.1:33189, id=81, length=311
User-Name = "6609876(a)10.0.1.22"
Digest-Attributes = 0x0a0936363039383736
Digest-Attributes = 0x010b31302e302e312e3232
Digest-Attributes =
0x022a34323366333163373062336631643261643330383833633238303434316632663133643136613830
Digest-Attributes = 0x040f7369703a31302e302e312e3232
Digest-Attributes = 0x030a5245474953544552
Digest-Attributes = 0x050661757468
Digest-Attributes = 0x090a3030303030303163
Digest-Attributes =
0x08224433343132424236394131453131443939334232303035304241373836433642
Digest-Response = "e4f68760f2b3eed0ad45942b32542c92"
Service-Type = IAPP-Register
Sip-URI-User = "6609876"
Cisco-AVPair = "call-id=D3412ADE9A1E11D993B20050BA786C6B(a)10.0.1.22"
NAS-IP-Address = 127.0.0.1
NAS-Port = 5060
Processing the authorize section of radiusd.conf
modcall: entering group authorize for request 10
modcall[authorize]: module "preprocess" returns ok for request 10
modcall[authorize]: module "chap" returns noop for request 10
modcall[authorize]: module "mschap" returns noop for request 10
rlm_digest: Converting Digest-Attributes to something sane...
Digest-User-Name = "6609876"
Digest-Realm = "10.0.1.22"
Digest-Nonce = "423f31c70b3f1d2ad30883c280441f2f13d16a80"
Digest-URI = "sip:10.0.1.22"
Digest-Method = "REGISTER"
Digest-QOP = "auth"
Digest-Nonce-Count = "0000001c"
Digest-CNonce = "D3412BB69A1E11D993B20050BA786C6B"
rlm_digest: Adding Auth-Type = DIGEST
modcall[authorize]: module "digest" returns ok for request 10
rlm_realm: Looking up realm "10.0.1.22" for User-Name = "6609876(a)10.0.1.22"
rlm_realm: No such realm "10.0.1.22"
modcall[authorize]: module "suffix" returns noop for request 10
rlm_eap: No EAP-Message, not doing EAP
modcall[authorize]: module "eap" returns noop for request 10
users: Matched DEFAULT at 152
modcall[authorize]: module "files" returns ok for request 10
modcall: group authorize returns ok for request 10
rad_check_password: Found Auth-Type DIGEST
auth: type "digest"
Processing the authenticate section of radiusd.conf
modcall: entering group authenticate for request 10
rlm_digest: Configuration item "User-Password" is required for authentication.
modcall[authenticate]: module "digest" returns invalid for request 10
modcall: group authenticate returns invalid for request 10
auth: Failed to validate the user.
Delaying request 10 for 1 seconds
Finished request 10
Going to the next request
Sending Access-Reject of id 80 to 127.0.0.1:33188
Waking up in 1 seconds...
--- Walking the entire request list ---
Waking up in 1 seconds...
--- Walking the entire request list ---
Sending Access-Reject of id 81 to 127.0.0.1:33189
Waking up in 2 seconds...
--- Walking the entire request list ---
Cleaning up request 8 ID 79 with timestamp 423f309b
Waking up in 1 seconds...
--- Walking the entire request list ---
Cleaning up request 9 ID 80 with timestamp 423f309c
Waking up in 1 seconds...
--- Walking the entire request list ---
Cleaning up request 10 ID 81 with timestamp 423f309d
Nothing to do. Sleeping until we see a request.
--
rrgv
We are all aware of SER limitation due the fact it is *only* transaction
statefull at proxy level, any dialog support being missing. There are
many features/services which actually requires a dialog awareness, like
storing the information in the dialog creation stage, information which
will be used during the whole dialog existence.
The most urging example is NAT traversal, in dealing with the within the
dialog INVITEs (re-INVITEs). When processing the initial INVITE, the
proxy detects if the caller or callee is behind some NAT and fixes the
signalling and media parts - since not all the detection mechanism are
available for within the dialog requests (like usrloc), to be able to
fix correspondingly the sequential requests, the proxy must remember
that the original request was NAT processed.
There are many other cases where dialog awareness fixes or helps....it's
not my goal to make a list here....
The solution is to store additional dialog-related information in the
routing set, headers which show up in all sequential requests; by
routing set I would say the Route hdrs and RURI; the routing set is
build based on Record-Route and Contact hdrs.
So far, the way to go was using the Contact header (via parameters) as
carrier. In most of the cases works, but this mechanism is not RFC
reinforced - I had a talk with Juha and he found some phones which
actually ignore the Contact hdr parameters, which is perfect allowed by
RFC....Not to mention that which Contact hdr (UAC or UAS) appear in
routing info depends on the request direction (UAC->UAS or UAS->UAC) and
Record-Route parameters are re-infonced by RFC (see 12.1.1 UAS behavior)
So the next try is to add this information in the Record-Route header
inserted by the proxy. Within the dialog, this information can be found
(with no direction dependencies) in Route header (with or proxy address)...
And here is a patch for the RR module which adds this functionality:
1) adding parameters to Record-Route; since RR is inserted during req.
processing (as lump), no textops function can be apply to it. So use the
add_rr_param("param") function - it can be called also before or after
the record_route() call; I would say it's more convenient from scripting
point of view (no dependencies between doing record_route() and adding
params);
2) checking Route parameters: this could be done via textops, but it
would be dangerous -> you have to identify the Route header
corresponding to your proxy (which will be quite difficult to put as
regexp), So use check_route_param("regexp") function which will check
the proper Route parameters against the given regexp. Must be call after
loose_route().
Basic scenario (example):
UAC SER
PROXY UAS
------ INVITE ----------> record_route() ---------
INVITE -------->
add_rr_param(";foo=true")
----- reINVITE ---------> loose_route() --------
reINVITE ------->
check_route_param(";foo=true")
<---- reINVITE ---------- loose_route() <-------
reINVITE --------
check_route_param(";foo=true")
<-------- BYE ----------- loose_route() <--------- BYE
-----------
check_route_param(";foo=true")
The patch doesn't alter any of the previous functionality.
Jan take a look and let's put it on CVS.
bogdan
Index: modules/rr/loose.c
===================================================================
RCS file: /cvsroot/ser/sip_router/modules/rr/loose.c,v
retrieving revision 1.36
diff -u -r1.36 loose.c
--- modules/rr/loose.c 23 Feb 2005 17:16:05 -0000 1.36
+++ modules/rr/loose.c 15 Apr 2005 14:52:55 -0000
@@ -30,6 +30,8 @@
* ---------
* 2003-02-28 scratchpad compatibility abandoned (jiri)
* 2003-01-27 next baby-step to removing ZT - PRESERVE_ZT (jiri)
+ * 2005-04-10 check_route_param() and all hooks for keeping reference to
+ * Route params added (bogdan)
*/
@@ -57,6 +59,10 @@
#define ROUTE_SUFFIX ">\r\n"
#define ROUTE_SUFFIX_LEN (sizeof(ROUTE_SUFFIX)-1)
+/* variables used to hook the param part of the local route -bogdan */
+static unsigned int routed_msg_id;
+static str routed_params = {0,0};
+
/*
* Test whether we are processing pre-loaded route set
@@ -511,12 +517,16 @@
if (is_myself(&puri.host, puri.port_no))
#endif
{
- /* if (enable_double_rr && is_2rr(&_ruri->params)) { */
- /* DBG("ras(): Removing 2nd URI of mine: '%.*s'\n", rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s)); */
+ /*if (enable_double_rr && is_2rr(&_ruri->params)) {
+ * DBG("ras(): Removing 2nd URI of mine: '%.*s'\n",
+ * rt->nameaddr.uri.len, ZSW(rt->nameaddr.uri.s)); */
+ /* set the hooks for the params -bogdan */
+ routed_msg_id = _m->id;
+ routed_params = puri.params;
if (!rt->next) {
- /* No next route in the same header, remove the whole header
- * field immediately
- */
+ /* No next route in the same header, remove the whole header
+ * field immediately
+ */
if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) {
LOG(L_ERR, "after_strict: Cannot remove Route HF\n");
return RR_ERROR;
@@ -661,11 +671,15 @@
if (is_myself(&puri.host, puri.port_no))
#endif
{
- DBG("after_loose: Topmost route URI: '%.*s' is me\n", uri->len, ZSW(uri->s));
+ DBG("after_loose: Topmost route URI: '%.*s' is me\n",
+ uri->len, ZSW(uri->s));
+ /* set the hooks for the params -bogdan */
+ routed_msg_id = _m->id;
+ routed_params = puri.params;
if (!rt->next) {
- /* No next route in the same header, remove the whole header
- * field immediately
- */
+ /* No next route in the same header, remove the whole header
+ * field immediately
+ */
if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) {
LOG(L_ERR, "after_loose: Can't remove Route HF\n");
return RR_ERROR;
@@ -684,16 +698,16 @@
if (enable_double_rr && is_2rr(&puri.params)) {
if (!rt->next) {
- /* No next route in the same header, remove the whole header
- * field immediately
- */
+ /* No next route in the same header, remove the whole header
+ * field immediately
+ */
if (!del_lump(_m, hdr->name.s - _m->buf, hdr->len, 0)) {
LOG(L_ERR, "after_loose: Can't remove Route HF\n");
return RR_ERROR;
}
res = find_next_route(_m, &hdr);
if (res < 0) {
- LOG(L_ERR, "after_loose: Error while finding next route\n");
+ LOG(L_ERR,"after_loose: Error while finding next route\n");
return RR_ERROR;
}
if (res > 0) { /* No next route found */
@@ -783,3 +797,32 @@
}
}
}
+
+
+
+int check_route_param(struct sip_msg * msg, char *re, char *foo)
+{
+ regmatch_t pmatch;
+ char bk;
+
+ /* check if the hooked params belong to the same message */
+ if (routed_msg_id != msg->id)
+ return -1;
+
+ /* check if params are present */
+ if ( !routed_params.s || !routed_params.len )
+ return -1;
+
+ /* do the well-known trick to convert to null terminted */
+ bk = routed_params.s[routed_params.len];
+ routed_params.s[routed_params.len] = 0;
+ DBG("DEBUG:rr:check_route_param: params are <%s>\n", routed_params.s);
+ if (regexec( (regex_t*)re, routed_params.s, 1, &pmatch, 0)!=0) {
+ routed_params.s[routed_params.len] = bk;
+ return -1;
+ } else {
+ routed_params.s[routed_params.len] = bk;
+ return 1;
+ }
+}
+
Index: modules/rr/loose.h
===================================================================
RCS file: /cvsroot/ser/sip_router/modules/rr/loose.h,v
retrieving revision 1.3
diff -u -r1.3 loose.h
--- modules/rr/loose.h 24 Aug 2004 09:00:38 -0000 1.3
+++ modules/rr/loose.h 15 Apr 2005 14:52:56 -0000
@@ -40,4 +40,6 @@
int loose_route(struct sip_msg* _m, char* _s1, char* _s2);
+int check_route_param(struct sip_msg * msg, char *re, char *foo);
+
#endif /* LOOSE_H */
Index: modules/rr/record.c
===================================================================
RCS file: /cvsroot/ser/sip_router/modules/rr/record.c,v
retrieving revision 1.14
diff -u -r1.14 record.c
--- modules/rr/record.c 24 Aug 2004 09:00:38 -0000 1.14
+++ modules/rr/record.c 15 Apr 2005 14:52:56 -0000
@@ -29,6 +29,7 @@
* History:
* -------
* 2003-04-04 Extracted from common.[ch] (janakj)
+ * 2005-04-10 add_rr_param() function and all corresponing hooks added (bogdan)
*/
#include <string.h>
@@ -63,6 +64,22 @@
#define INBOUND 1 /* Insert inbound Record-Route */
#define OUTBOUND 0 /* Insert outbound Record-Route */
+#define RR_PARAM_BUF_SIZE 512
+
+static unsigned int last_rr_msg;
+
+/* RR suffix lump and offset - used for inserting RR params
+ * after RR was done; use a two values array for INBOUND and
+ * OUTBOUND RR -bogdan */
+static struct lump *rr_suffix_lump[2] = {0,0};
+static int rr_suffix_end_offset[2] = {0,0};
+
+/* RR param buffer - usd for storing RR param which are
+ * added before RR insertion -bogdan */
+static char rr_param_buf_ptr[RR_PARAM_BUF_SIZE];
+static str rr_param_buf = {rr_param_buf_ptr,0};
+static unsigned int rr_param_msg;
+
/*
* Extract username from the Request URI
@@ -104,16 +121,20 @@
{
char* prefix, *suffix, *crlf, *r2;
int suffix_len, prefix_len;
+ char *p;
prefix_len = RR_PREFIX_LEN + (user->len ? (user->len + 1) : 0);
prefix = pkg_malloc(prefix_len);
if (enable_full_lr) {
suffix_len = (_lr ? RR_LR_FULL_TERM_LEN : RR_SR_TERM_LEN) +
- ((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0);
+ ((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0) +
+ rr_param_buf.len;
} else {
suffix_len = (_lr ? RR_LR_TERM_LEN : RR_SR_TERM_LEN) +
- ((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0);
+ ((tag && tag->len) ? (RR_FROMTAG_LEN + tag->len) : 0) +
+ rr_param_buf.len;
}
+ rr_suffix_end_offset[_inbound] = RR_SR_TERM_LEN;
suffix = pkg_malloc(suffix_len);
crlf = pkg_malloc(2);
@@ -145,21 +166,24 @@
#endif
prefix[RR_PREFIX_LEN + user->len] = '@';
}
-
+
+ p = suffix;
if (tag && tag->len) {
- memcpy(suffix, RR_FROMTAG, RR_FROMTAG_LEN);
- memcpy(suffix + RR_FROMTAG_LEN, tag->s, tag->len);
- if (enable_full_lr) {
- memcpy(suffix + RR_FROMTAG_LEN + tag->len, _lr ? RR_LR_FULL_TERM : RR_SR_TERM, _lr ? RR_LR_FULL_TERM_LEN : RR_SR_TERM_LEN);
- } else {
- memcpy(suffix + RR_FROMTAG_LEN + tag->len, _lr ? RR_LR_TERM : RR_SR_TERM, _lr ? RR_LR_TERM_LEN : RR_SR_TERM_LEN);
- }
+ memcpy(p, RR_FROMTAG, RR_FROMTAG_LEN);
+ p += RR_FROMTAG_LEN;
+ memcpy(p, tag->s, tag->len);
+ p += tag->len;
+ }
+ if (rr_param_buf.len) {
+ memcpy( p, rr_param_buf.s, rr_param_buf.len);
+ p += rr_param_buf.len;
+ }
+ if (enable_full_lr) {
+ memcpy( p, _lr ? RR_LR_FULL_TERM : RR_SR_TERM,
+ _lr ? RR_LR_FULL_TERM_LEN : RR_SR_TERM_LEN);
} else {
- if (enable_full_lr) {
- memcpy(suffix, _lr ? RR_LR_FULL_TERM : RR_SR_TERM, _lr ? RR_LR_FULL_TERM_LEN : RR_SR_TERM_LEN);
- } else {
- memcpy(suffix, _lr ? RR_LR_TERM : RR_SR_TERM, _lr ? RR_LR_TERM_LEN : RR_SR_TERM_LEN);
- }
+ memcpy( p, _lr ? RR_LR_TERM : RR_SR_TERM,
+ _lr ? RR_LR_TERM_LEN : RR_SR_TERM_LEN);
}
memcpy(crlf, CRLF, 2);
@@ -177,6 +201,7 @@
r2 = 0;
}
if (!(_l2 = insert_new_lump_before(_l2, suffix, suffix_len, 0))) goto lump_err;
+ rr_suffix_lump[_inbound] = _l2;
suffix = 0;
if (!(_l2 = insert_new_lump_before(_l2, crlf, 2, 0))) goto lump_err;
crlf = 0;
@@ -225,6 +250,12 @@
tag = 0;
}
+ if (rr_param_buf.len && rr_param_msg!=_m->id) {
+ /* rr_params were set for a different message -> reset buffer */
+ rr_param_buf.len = 0;
+ DBG("----> reset rr_param_buf\n");
+ }
+
if (enable_double_rr) {
l = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, 0);
l2 = anchor_lump(_m, _m->headers->name.s - _m->buf, 0, 0);
@@ -256,6 +287,8 @@
return -4;
}
+ /* reset the rr_param buffer */
+ rr_param_buf.len = 0;
return 0;
}
@@ -266,8 +299,6 @@
*/
static inline int do_RR(struct sip_msg* _m, int _lr)
{
- static unsigned int last_rr_msg;
-
if (_m->id == last_rr_msg) {
LOG(L_ERR, "record_route(): Double attempt to record-route\n");
return -1;
@@ -395,3 +426,72 @@
{
return do_RR(_m, 0);
}
+
+
+/*
+ * Appends a string to an existent lump
+ */
+static inline int append_to_lump( struct lump *l, str *s, int end_offset)
+{
+ char *p;
+
+ /* update buffer */
+ p = pkg_realloc( l->u.value, l->len+s->len);
+ if (p==0) {
+ LOG(L_ERR,"ERROR:rr:add_rr_param: no more pkg memory\n");
+ return -1;
+ }
+ memmove(p+l->len-end_offset+s->len, p+l->len-end_offset, end_offset);
+ memcpy( p+l->len-end_offset, s->s, s->len);
+ /* update lump structure */
+ l->len += s->len;
+ l->u.value = p;
+ return 0;
+}
+
+
+/*
+ * Appends a new Record-Route parameter
+ */
+int add_rr_param(struct sip_msg* msg, char* param, char* foo)
+{
+ str *rr_param;
+ int i;
+
+ rr_param = (str*)param;
+
+ if (last_rr_msg==msg->id) {
+ /* RR was already done -> have to modify the RR-sufix lump */
+ for( i=OUTBOUND ; i<=INBOUND ; i++ ) {
+ if ( !rr_suffix_lump[i] || !rr_suffix_end_offset[i] )
+ continue;
+ if (append_to_lump( rr_suffix_lump[i], rr_param,
+ rr_suffix_end_offset[i])!=0) {
+ LOG(L_ERR,"ERROR:rr:add_rr_param: failed to update lump\n");
+ goto error;
+ }
+ }
+ } else {
+ /* RR not done yet -> store the param in the static buffer */
+ if (rr_param_msg!=msg->id) {
+ /* it's about a different messge -> reset buffer */
+ rr_param_buf.len = 0;
+ rr_param_msg = msg->id;
+ }
+ if (rr_param_buf.len+rr_param->len>RR_PARAM_BUF_SIZE) {
+ LOG(L_ERR,"ERROR:rr:add_rr_param: maximum size of "
+ "rr_param_buf exceeded\n");
+ goto error;
+ }
+ memcpy( rr_param_buf.s+rr_param_buf.len, rr_param->s, rr_param->len);
+ rr_param_buf.len += rr_param->len;
+ DBG("DEBUG:rr:add_rr_param: rr_param_buf=<%.*s>\n",rr_param_buf.len,
+ rr_param_buf.s);
+ }
+ return 1;
+
+error:
+ return -1;
+}
+
+
Index: modules/rr/record.h
===================================================================
RCS file: /cvsroot/ser/sip_router/modules/rr/record.h,v
retrieving revision 1.2
diff -u -r1.2 record.h
--- modules/rr/record.h 24 Aug 2004 09:00:38 -0000 1.2
+++ modules/rr/record.h 15 Apr 2005 14:52:56 -0000
@@ -56,4 +56,10 @@
int record_route_strict(struct sip_msg* _m, char* _s1, char* _s2);
+/*
+ * Appends a new Record-Route parameter
+ */
+int add_rr_param(struct sip_msg* msg, char* param, char* foo);
+
+
#endif /* RECORD_H */
Index: modules/rr/rr_mod.c
===================================================================
RCS file: /cvsroot/ser/sip_router/modules/rr/rr_mod.c,v
retrieving revision 1.33
diff -u -r1.33 rr_mod.c
--- modules/rr/rr_mod.c 24 Aug 2004 09:00:38 -0000 1.33
+++ modules/rr/rr_mod.c 15 Apr 2005 14:52:57 -0000
@@ -33,11 +33,14 @@
* 2003-03-19 all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei)
* 2003-04-01 Added record_route with ip address parameter (janakj)
* 2003-04-14 enable_full_lr parameter introduced (janakj)
+ * 2005-04-10 add_rr_param() and check_route_param() added (bogdan)
*/
#include <stdio.h>
#include <stdlib.h>
+#include <regex.h>
+
#include "../../sr_module.h"
#include "../../ut.h"
#include "../../error.h"
@@ -61,6 +64,7 @@
static int mod_init(void);
static int str_fixup(void** param, int param_no);
+static int regexp_fixup(void** param, int param_no);
/*
@@ -74,10 +78,18 @@
* Oh, BTW, have I mentioned already that you shouldn't use strict routing ?
*/
static cmd_export_t cmds[] = {
- {"loose_route", loose_route, 0, 0, REQUEST_ROUTE},
- {"record_route", record_route, 0, 0, REQUEST_ROUTE},
- {"record_route_preset", record_route_preset, 1, str_fixup, REQUEST_ROUTE},
- {"record_route_strict" , record_route_strict, 0, 0, 0 },
+ {"loose_route", loose_route, 0, 0,
+ REQUEST_ROUTE},
+ {"record_route", record_route, 0, 0,
+ REQUEST_ROUTE},
+ {"record_route_preset", record_route_preset, 1, str_fixup,
+ REQUEST_ROUTE},
+ {"record_route_strict" , record_route_strict, 0, 0,
+ 0},
+ {"add_rr_param", add_rr_param, 1, str_fixup,
+ REQUEST_ROUTE},
+ {"check_route_param", check_route_param, 1, regexp_fixup,
+ REQUEST_ROUTE},
{0, 0, 0, 0, 0}
};
@@ -149,3 +161,29 @@
return 0;
}
+
+
+static int regexp_fixup(void** param, int param_no)
+{
+ regex_t* re;
+
+ if (param_no==1) {
+ if ((re=pkg_malloc(sizeof(regex_t)))==0) {
+ LOG(L_ERR,"ERROR:rr:regexp_fixup: no more pkg memory\n");
+ return E_OUT_OF_MEM;
+ }
+ if (regcomp(re, *param, REG_EXTENDED|REG_ICASE|REG_NEWLINE) ) {
+ pkg_free(re);
+ LOG(L_ERR, "ERROR:rr:regexp_fixup: bad regexp %s\n",(char*)*param);
+ return E_BAD_RE;
+ }
+ /* free string */
+ pkg_free(*param);
+ /* replace it with the compiled re */
+ *param=re;
+ }
+ return 0;
+}
+
+
+
Patrick,
This is a bash script that externnotify executes
#!/bin/ash
#
# This script is called by Asterisk every time a voice mail is
# left for a subscriber. The CONTEXT, EXTENSION, and VM_COUNT
# are passed by Asterisk.
#
# All we need to do is update a file in /var/spool/asterisk/mwi
# to tell our cron job that we have to send a SIP NOTIFY message
# to a particular UA
#
# This file is referenced in /etc/asterisk/voicemail.conf
#
# NOTE: The $2 argument is passed in to this script in the
# following form: MAILBOX@DOMAIN
#
# example: 4075551212@default
#
MWI_DIR=/var/spool/asterisk/mwi/
if [ -n ${2} ] ; then
if /bin/touch ${MWI_DIR}/${2} ; then
exit 0
else
exit 1
fi
else
exit 2
fi
The following is executed in CRON every minute. This script actually
notifies the subscribers by sending NOTIFY messages to SER. Please
note that our asterisk is a modified version that stores mailboxes in
a directory hash, so you can modify this to fit your needs.
#!/bin/sh
# This script send the SIP NOTIFY message to UA. The NOTIFY message
# can either enable or disable the UA message indicator. The script
# looks for any file in $VM_HOME and creates the NOTIFY message
#
# The actual NOTIFY message is sent to the SIP proxy by the sipsak
# utilty. After the message is send we delete the processed file
# from VM_HOME so we don't keep resending the same message. Most
# UAs will periodically send a SUBSCRIBE message to the SIP proxy.
# When this occurs the SIP proxy will place a file in the location
# /var/spool/mwi/delay and a less frequent CRON job will process it.
VM_ROOT=/var/spool/asterisk/voicemail
VM_HOME=/var/spool/asterisk/mwi
SIP_SERVER=71.110.20.33
SIP_PORT=5060
SIP_FROM=vmserver
SIP_PASSWORD=heslo
DB_NAME=ser
DB_SERVER=10.255.237.18
DB_OPTIONS="--skip-column-names --connect_timeout=3"
DB_USER=ser
DB_PASSWORD=heslo
# Allow glob expansions to return NULL
shopt -s nullglob
# Sanity check that VM_HOME exists, warn if it does not
if [ ! -d $VM_HOME ] ; then
echo "Cannot chdir to VM_HOME, exiting"
exit 1
fi
for file in $VM_HOME/*
do
file=$( basename $file @default )
# The file existance check may seem redundant, but it is
theoretically possible
# for this script to take longer than 1 cron cycle to run, or
for it to be run
# by hand while another copy is running, so we really do need
this check.
if [ -e ${VM_HOME}/${file}@default ] && [ ${#file} -eq 10 ]; then
QUERY="SELECT subscriber.domain FROM subscriber,
location WHERE subscriber.username='$file' AND subscriber.username =
location.username LIMIT 1"
DOMAIN=`mysql ${DB_OPTIONS} -h ${DB_SERVER}
-u${DB_USER} -p${DB_PASSWORD} -e "$QUERY" ${DB_NAME}`
# If the MySQL call fails, then $DOMAIN will hold the
error message instead
if [ $? -ne 0 ] ; then
echo "Error connecting to MySQL: $DOMAIN"
exit 2
fi
if [ -n "${DOMAIN}" ]; then
MAILBOX=$VM_ROOT/default/${file:0:3}/${file:3:3}/${file:6:4}
# Sanity check that INBOX exists, warn if it does not
if [ ! -d ${MAILBOX} ]; then
echo "${MAILBOX} does not exist"
continue
fi
# Delete all messages with a zero length. If
the subscriber still
# has voice mail files in their INBOX after
this step then notify
# them even if the total is zero, which will
extinguish their MWI
for message in ${MAILBOX}/INBOX/*.gsm
do
if [ ! -s $message ]; then
GSM=`basename $message .gsm`
rm -f ${MAILBOX}/INBOX/$GSM.*
fi
done
TOTAL_MESSAGES=0
NEW_MESSAGES=0
OLD_MESSAGES=0
if [ -d ${MAILBOX}/INBOX ]; then
tmp=( $( echo ${MAILBOX}/INBOX/*.txt ) )
NEW_MESSAGES=${#tmp[*]}
else
NEW_MESSAGES=0
fi
if [ -d ${MAILBOX}/Old ]; then
tmp=( $( echo ${MAILBOX}/Old/*.txt ) )
OLD_MESSAGES=${#tmp[*]}
else
OLD_MESSAGES=0
fi
TOTAL_MESSAGES=$(($NEW_MESSAGES + $OLD_MESSAGES))
if [ "$NEW_MESSAGES" == "0" ]; then
HAS_NEW="no"
else
HAS_NEW="yes"
fi
CONTENT_LENGTH=$(( 34 + ${#HAS_NEW} +
${#NEW_MESSAGES} + ${#OLD_MESSAGES} ))
SEQUENCE=$( printf "%06d" $RANDOM )
(
cat <<-EOM
NOTIFY sip:${file}@${DOMAIN} SIP/2.0
From: <sip:${SIP_FROM}@${DOMAIN}>
To: <sip:${file}@${DOMAIN}>
Contact: <sip:${file}@${SIP_SERVER}>
Call-ID: ${SEQUENCE}@${SIP_SERVER}
CSeq: ${SEQUENCE} NOTIFY
Event: message-summary
Content-Type: application/simple-message-summary
Content-Length: ${CONTENT_LENGTH}
Messages-Waiting: ${HAS_NEW}
Voicemail: ${NEW_MESSAGES}/${OLD_MESSAGES}
EOM
) | /usr/bin/sipsak shoot -a ${SIP_PASSWORD} -s sip:${file}@${DOMAIN} -f -
if [ $? -ne 0 ] ; then
echo "sipsak error for $file"
fi
else
echo "DOMAIN is null for $file"
fi
else
if [ ${#file} -ne 10 ] ; then
echo "Extension was not 10 digits: '$file' (${#file})"
else
echo "Problem with file: ${VM_HOME}/${file}@default"
fi
fi
rm ${VM_HOME}/${file}@default
done
exit 0
On Tue, 1 Mar 2005 10:06:56 -0500, Patrick Baker
<patricksbaker(a)gmail.com> wrote:
> could you show me an example of the sipsak command for the cisco phone
> - I can then just make an agi that will call it after vm is left.
>
> Best regards,
>
> Patrick B
>
>
> On Tue, 1 Mar 2005 10:03:35 -0500, Java Rockx <javarockx(a)gmail.com> wrote:
> > Since the SIP UA is registered with SER you'll need to have Asterisk
> > send a NOTIFY message to the user by means of SER.
> >
> > So you can send the NOTIFY to SER with a <From:> header as asterisk
> > and the <To> / R-URI as the Cisco UA.
> >
> > FYI, we do this using sipsak to send the NOTIFY to ser. This is
> > triggered either using the externnotify parameter in the Asterisk
> > voicemail.conf file and by CRON for periodic MWI updates.
> >
> > Regards,
> > Paul
> >
> >
> > On Tue, 1 Mar 2005 09:53:43 -0500, Patrick Baker
> > <patricksbaker(a)gmail.com> wrote:
> > > Hello,
> > >
> > > I'm sure this may have been asked but, I just wanted clarify how this
> > > would be accomplished. I'm looking to have the MWI light on my cisco
> > > phone turn on when a message is left in VM.
> > >
> > > Here is my current situation:
> > >
> > > sip user -> ser -> asterisk
> > >
> > > Best regards,
> > >
> > > Patrick b.
> > >
> > > _______________________________________________
> > > Serusers mailing list
> > > serusers(a)lists.iptel.org
> > > http://lists.iptel.org/mailman/listinfo/serusers
> > >
> >
>
Hi,
from my debug after trying to use the uac_auth in a failure route, I get the following:
Mar 31 16:37:46 sip ser[20394]: BUG:uac:uac_auth: empty reply on picked branch
I've modified the cvs version to compile on 0.9.0
Anyone?
Mvh,
Helge Waastad
I have a SER Proxy that is routing calls to a SIP Redirector which then
informs SER as to which gateway to send call to.
GW1 GW2
| |
SERProxy------->SIP Redirector
|
IP Phone
I would like for SER to take a 302 redirect and then proxy the call to
the gateway per the redirect..however, SER is currently serving the
redirect to the IP Phone..which is unauthorized to access the gateway.
How do I configure SER to actually proxy the call per the redirect on
behalf of the IP Phone?
Hi!
I want to use rtpproxy in remote mode, i.e. rtpproxy runs on another
machine as ser.
I've tried to start rtpproxy accordingly to the README.remote with:
./rtpproxy -s udp:213.10.10.10
But I only get this error response:
rtpproxy: explicit binding address has to be specified in UDP command mode
What am I doing wrong?
Alex Mack