From c55ec2ba2dcbcfda504339bfd8070b000c9bee52 Mon Sep 17 00:00:00 2001 From: Charles Chance Date: Tue, 21 Apr 2015 21:48:53 +0100 Subject: [PATCH] dmq: resolve multiple IPv4/IPv6 addresses for a single notification address - optionally enabled by new parameter "multi_notify" - includes addresses from DNS SRV records, A and AAAA records - patch provided by Robert Boisvert on sr-dev list --- modules/dmq/dmq.c | 2 + modules/dmq/dmq.h | 1 + modules/dmq/doc/dmq_admin.xml | 23 +++ modules/dmq/notification_peer.c | 282 ++++++++++++++++++++++++++++++-- 4 files changed, 293 insertions(+), 15 deletions(-) diff --git a/modules/dmq/dmq.c b/modules/dmq/dmq.c index fd0c30e334a..7e1ac351852 100644 --- a/modules/dmq/dmq.c +++ b/modules/dmq/dmq.c @@ -63,6 +63,7 @@ str dmq_server_socket = {0, 0}; struct sip_uri dmq_server_uri; str dmq_notification_address = {0, 0}; +int multi_notify = 0; struct sip_uri dmq_notification_uri; int ping_interval = 60; @@ -110,6 +111,7 @@ static param_export_t params[] = { {"ping_interval", INT_PARAM, &ping_interval}, {"server_address", PARAM_STR, &dmq_server_address}, {"notification_address", PARAM_STR, &dmq_notification_address}, + {"multi_notify", INT_PARAM, &multi_notify}, {0, 0, 0} }; diff --git a/modules/dmq/dmq.h b/modules/dmq/dmq.h index ac119391f0e..d8781915a59 100644 --- a/modules/dmq/dmq.h +++ b/modules/dmq/dmq.h @@ -45,6 +45,7 @@ extern str dmq_request_method; extern str dmq_server_socket; extern struct sip_uri dmq_server_uri; extern str dmq_notification_address; +extern int multi_notify; extern struct sip_uri dmq_notification_uri; /* sl and tm */ extern struct tm_binds tmb; diff --git a/modules/dmq/doc/dmq_admin.xml b/modules/dmq/doc/dmq_admin.xml index e91995b6427..14b4a8e570b 100644 --- a/modules/dmq/doc/dmq_admin.xml +++ b/modules/dmq/doc/dmq_admin.xml @@ -128,6 +128,29 @@ modparam("dmq", "server_address", "sip:10.0.0.20:5060") ... modparam("dmq", "notification_address", "sip:10.0.0.21:5060") ... + + + +
+ <varname>multi_notify</varname>(int) + + Enables the ability to resolve multiple IPv4/IPv6 addresses for + a single notification address. + + + A value of zero resolves to the first IP address found. + A non-zero value resolves to all IP addresses associated with the host. + This includes addresses from DNS SRV records, A and AAAA records. + + + Default value is 0. + + + Set <varname>multi_notify</varname> parameter + +... +modparam("dmq", "multi_notify", 1) +...
diff --git a/modules/dmq/notification_peer.c b/modules/dmq/notification_peer.c index 78180761452..416b6e18b3a 100644 --- a/modules/dmq/notification_peer.c +++ b/modules/dmq/notification_peer.c @@ -24,6 +24,9 @@ #include "notification_peer.h" +#define MAXDMQURILEN 255 +#define MAXDMQHOSTS 30 + str notification_content_type = str_init("text/plain"); dmq_resp_cback_t notification_callback = {¬ification_resp_callback_f, 0}; @@ -62,27 +65,276 @@ int add_notification_peer() return -1; } +/********** +* Create IP URI +* +* INPUT: +* Arg (1) = container for hosts +* Arg (2) = host index +* Arg (3) = host name pointer +* Arg (4) = host name length +* Arg (5) = parsed URI pointer +* OUTPUT: 0=unable to create URI +**********/ + +int create_IP_uri (char **puri_list, int host_index, char *phost, + int hostlen, sip_uri_t *puri) + +{ + int pos; + char *plist; + char *perr = "notification host count reached max!\n"; + + /********** + * insert + * o scheme + * o user name/password + * o host + * o port + * o parameters + **********/ + + plist = puri_list [host_index]; + if (puri->type == SIPS_URI_T) { + strncpy (plist, "sips:", 5); + pos = 5; + } else { + strncpy (plist, "sip:", 4); + pos = 4; + } + if (puri->user.s) { + strncpy (&plist [pos], puri->user.s, puri->user.len); + pos += puri->user.len; + if (puri->passwd.s) { + plist [pos++] = ':'; + strncpy (&plist [pos], puri->passwd.s, puri->passwd.len); + pos += puri->passwd.len; + } + plist [pos++] = '@'; + } + if ((pos + hostlen) > MAXDMQURILEN) { + LM_WARN ("%s", perr); + return 0; + } + strncpy (&plist [pos], phost, hostlen); + pos += hostlen; + if (puri->port_no) { + if ((pos + 6) > MAXDMQURILEN) { + LM_WARN ("%s", perr); + return 0; + } + plist [pos++] = ':'; + pos += ushort2sbuf (puri->port_no, &plist [pos], 5); + } + if (puri->params.s) { + if ((pos + puri->params.len) >= MAXDMQURILEN) { + LM_WARN ("%s", perr); + return 0; + } + plist [pos++] = ';'; + strncpy (&plist [pos], puri->params.s, puri->params.len); + pos += puri->params.len; + } + plist [pos] = '\0'; + return 1; +} + +/********** +* Get DMQ Host List +* +* INPUT: +* Arg (1) = container for hosts +* Arg (2) = maximum number of hosts +* Arg (3) = host string pointer +* Arg (4) = parsed URI pointer +* Arg (5) = search SRV flag +* OUTPUT: number of hosts found +**********/ + +int get_dmq_host_list (char **puri_list, int max_hosts, str *phost, + sip_uri_t *puri, int bSRV) + +{ + int host_cnt, len; + unsigned short origport, port; + str pstr [1]; + char pname [256], pIP [IP6_MAX_STR_SIZE + 2]; + struct rdata *phead, *prec; + struct srv_rdata *psrv; + + /********** + * o IP address? + * o make null terminated name + * o search SRV? + **********/ + + if (str2ip (phost) || str2ip6 (phost)) { + if (!create_IP_uri (puri_list, 0, phost->s, phost->len, puri)) { + LM_DBG ("adding DMQ node IP host %.*s=%s\n", + phost->len, phost->s, puri_list [0]); + return 0; + } + return 1; + } + strncpy (pname, phost->s, phost->len); + pname [phost->len] = '\0'; + host_cnt = 0; + if (bSRV) { + /********** + * get SRV records + **********/ + + port = puri->port_no; + phead = get_record (pname, T_SRV, RES_ONLY_TYPE); + for (prec = phead; prec; prec = prec->next) { + /********** + * o matching port? + * o check max + * o save original port + * o check target + * o restore port + **********/ + + psrv = (struct srv_rdata *) prec->rdata; + if (port && (port != psrv->port)) + { continue; } + if (host_cnt == max_hosts) { + LM_WARN ("notification host count reached max!\n"); + free_rdata_list (phead); + return host_cnt; + } + pstr->s = psrv->name; + pstr->len = psrv->name_len; + origport = puri->port_no; + puri->port_no = psrv->port; + host_cnt += get_dmq_host_list (&puri_list [host_cnt], + MAXDMQHOSTS - host_cnt, pstr, puri, 0); + puri->port_no = origport; + } + if (phead) + free_rdata_list (phead); + } + + /********** + * get A records + **********/ + + phead = get_record (pname, T_A, RES_ONLY_TYPE); + for (prec = phead; prec; prec = prec->next) { + /********** + * o check max + * o create URI + **********/ + + if (host_cnt == max_hosts) { + LM_WARN ("notification host count reached max!\n"); + free_rdata_list (phead); + return host_cnt; + } + len = ip4tosbuf (((struct a_rdata *) prec->rdata)->ip, + pIP, IP4_MAX_STR_SIZE); + pIP [len] = '\0'; + if (create_IP_uri (puri_list, host_cnt, pIP, len, puri)) { + LM_DBG ("adding DMQ node A host %s=%s\n", pname, puri_list [host_cnt]); + host_cnt++; + } + } + if (phead) + free_rdata_list (phead); + + /********** + * get AAAA records + **********/ + + phead = get_record (pname, T_AAAA, RES_ONLY_TYPE); + for (prec = phead; prec; prec = prec->next) { + /********** + * o check max + * o create URI + **********/ + + if (host_cnt == max_hosts) { + LM_WARN ("notification host count reached max!\n"); + free_rdata_list (phead); + return host_cnt; + } + pIP [0] = '['; + len = ip6tosbuf (((struct aaaa_rdata *) prec->rdata)->ip6, + &pIP [1], IP6_MAX_STR_SIZE) + 1; + pIP [len++] = ']'; + pIP [len] = '\0'; + if (create_IP_uri (puri_list, host_cnt, pIP, len, puri)) { + LM_DBG + ("adding DMQ node AAAA host %s=%s\n", pname, puri_list [host_cnt]); + host_cnt++; + } + } + if (phead) + free_rdata_list (phead); + return host_cnt; +} + /** * @brief add a server node and notify it */ -dmq_node_t* add_server_and_notify(str* server_address) +dmq_node_t* add_server_and_notify(str *paddr) { - /* add the notification server to the node list - if any */ - dmq_node_t* node; - - node = add_dmq_node(node_list, server_address); - if(!node) { - LM_ERR("error adding notification node\n"); - goto error; + char puri_data [MAXDMQHOSTS * (MAXDMQURILEN + 1)]; + char *puri_list [MAXDMQHOSTS]; + dmq_node_t *pfirst, *pnode; + int host_cnt, index; + sip_uri_t puri [1]; + str pstr [1]; + + /********** + * o init data area + * o get list of hosts + * o process list + **********/ + + if (!multi_notify) { + pfirst = add_dmq_node (node_list, paddr); + } else { + /********** + * o init data area + * o get list of hosts + * o process list + **********/ + + for (index = 0; index < MAXDMQHOSTS; index++) { + puri_list [index] = &puri_data [index * (MAXDMQURILEN + 1)]; + } + if (parse_uri (paddr->s, paddr->len, puri) < 0) { + /* this is supposed to be good but just in case... */ + LM_ERR ("add_server_and_notify address invalid\n"); + return 0; + } + pfirst = NULL; + host_cnt = + get_dmq_host_list (puri_list, MAXDMQHOSTS, &puri->host, puri, 1); + for (index = 0; index < host_cnt; index++) { + pstr->s = puri_list [index]; + pstr->len = strlen (puri_list [index]); + pnode = add_dmq_node (node_list, pstr); + if (pnode && !pfirst) + { pfirst = pnode; } + } } - /* request initial list from the notification server */ - if(request_nodelist(node, 2) < 0) { - LM_ERR("error requesting initial nodelist\n"); - goto error; + + /********** + * o found at least one? + * o request node list + **********/ + + if (!pfirst) { + LM_ERR ("error adding notification node\n"); + return NULL; } - return node; -error: - return NULL; + if (request_nodelist (pfirst, 2) < 0) { + LM_ERR ("error requesting initial nodelist\n"); + return NULL; + } + return pfirst; } /**