ipops: new function dns_query(hostname, pvid)
authorDaniel-Constantin Mierla <miconda@gmail.com>
Mon, 15 Jul 2013 21:32:33 +0000 (23:32 +0200)
committerDaniel-Constantin Mierla <miconda@gmail.com>
Mon, 15 Jul 2013 21:32:33 +0000 (23:32 +0200)
- store the result of dns query in a variable $dns(pvid=>key)
- dns query is using getaddrinfo()
- $dns(pvid=>key) - new pv allowing to navigate through the result of a dns query
- key can be:
- count - number of addresses
- ipv4 - set to 1 if at least one ipv4 address (otherwise 0)
- ipv6 - set to 1 if at least one ipv6 address (otherwise 0)
- addr[index] - the address as string from position index in the list (0 based indexing)
- type[index] - the type of address from position index in the list (0 based indexing), the value is 4 for ipv4 and 6 for ipv6
- example:

if(dns_query("test.com", "xyz"))
{
xlog("===== number of addresses: $dns(xyz=>count)\n");
xlog("===== ipv4 address found: $dns(xyz=>ipv4)\n");
xlog("===== ipv6 address found: $dns(xyz=>ipv6)\n");
$var(i) = 0;
while($var(i)<$dns(xyz=>count)) {
xlog("===== #[$var(i)] type ($dns(xyz=>addr[$var(i)])) addr [$dns(xyz=>addr[$var(i)])]\n");
$var(i) = $var(i) + 1;
}
}

modules/ipops/ipops_mod.c
modules/ipops/ipops_pv.c [new file with mode: 0644]
modules/ipops/ipops_pv.h [new file with mode: 0644]

index dc58fc8..c87a0eb 100644 (file)
@@ -51,6 +51,7 @@
 #include "../../pvar.h"
 #include "../../resolve.h"
 #include "api.h"
 #include "../../pvar.h"
 #include "../../resolve.h"
 #include "api.h"
+#include "ipops_pv.h"
 #include "ip_parser.h"
 #include "rfc1918_parser.h"
 
 #include "ip_parser.h"
 #include "rfc1918_parser.h"
 
@@ -90,6 +91,13 @@ static int w_ip_is_in_subnet(struct sip_msg*, char*, char*);
 static int w_dns_sys_match_ip(sip_msg_t*, char*, char*);
 static int w_dns_int_match_ip(sip_msg_t*, char*, char*);
 
 static int w_dns_sys_match_ip(sip_msg_t*, char*, char*);
 static int w_dns_int_match_ip(sip_msg_t*, char*, char*);
 
+static int w_dns_query(struct sip_msg* msg, char* str1, char* str2);
+
+static pv_export_t mod_pvs[] = {
+       { {"dns", sizeof("dns")-1}, PVT_OTHER, pv_get_dns, 0,
+               pv_parse_dns_name, 0, 0, 0 },
+       { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
 
 /*
  * Exported functions
 
 /*
  * Exported functions
@@ -120,6 +128,8 @@ static cmd_export_t cmds[] =
   ANY_ROUTE },
   { "dns_int_match_ip", (cmd_function)w_dns_int_match_ip, 2, fixup_spve_spve, 0,
   ANY_ROUTE },
   ANY_ROUTE },
   { "dns_int_match_ip", (cmd_function)w_dns_int_match_ip, 2, fixup_spve_spve, 0,
   ANY_ROUTE },
+  { "dns_query", (cmd_function)w_dns_query, 2, fixup_spve_spve, 0,
+  ANY_ROUTE },
   { "bind_ipops", (cmd_function)bind_ipops, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0 }
 };
   { "bind_ipops", (cmd_function)bind_ipops, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0 }
 };
@@ -135,7 +145,7 @@ struct module_exports exports = {
   0,                         /*!< exported parameters */
   0,                         /*!< exported statistics */
   0,                         /*!< exported MI functions */
   0,                         /*!< exported parameters */
   0,                         /*!< exported statistics */
   0,                         /*!< exported MI functions */
-  0,                         /*!< exported pseudo-variables */
+  mod_pvs,                   /*!< exported pseudo-variables */
   0,                         /*!< extra processes */
   0,                         /*!< module initialization function */
   (response_function) 0,     /*!< response handling function */
   0,                         /*!< extra processes */
   0,                         /*!< module initialization function */
   (response_function) 0,     /*!< response handling function */
@@ -652,6 +662,8 @@ static int w_dns_sys_match_ip(sip_msg_t *msg, char *hnp, char *ipp)
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC; /* allow any of AF_INET or AF_INET6 */
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC; /* allow any of AF_INET or AF_INET6 */
+       // hints.ai_socktype = SOCK_STREAM;
+       hints.ai_socktype = SOCK_DGRAM;
 
        if ((status = getaddrinfo(hns.s, NULL, &hints, &res)) != 0)
        {
 
        if ((status = getaddrinfo(hns.s, NULL, &hints, &res)) != 0)
        {
@@ -732,3 +744,31 @@ static int w_dns_int_match_ip(sip_msg_t *msg, char *hnp, char *ipp)
        /* no match */
        return -1;
 }
        /* no match */
        return -1;
 }
+
+/**
+ *
+ */
+static int w_dns_query(struct sip_msg* msg, char* str1, char* str2)
+{
+       str hostname;
+       str name;
+
+       if(msg==NULL)
+       {
+               LM_ERR("received null msg\n");
+               return -1;
+       }
+
+       if(fixup_get_svalue(msg, (gparam_t*)str1, &hostname)<0)
+       {
+               LM_ERR("cannot get the hostname\n");
+               return -1;
+       }
+       if(fixup_get_svalue(msg, (gparam_t*)str2, &name)<0)
+       {
+               LM_ERR("cannot get the pv container name\n");
+               return -1;
+       }
+
+       return dns_update_pv(&hostname, &name);
+}
diff --git a/modules/ipops/ipops_pv.c b/modules/ipops/ipops_pv.c
new file mode 100644 (file)
index 0000000..e1b38e5
--- /dev/null
@@ -0,0 +1,428 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2013 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "../../dprint.h"
+#include "../../hashes.h"
+#include "../../pvar.h"
+
+
+#define PV_DNS_ADDR 64
+#define PV_DNS_RECS 32
+
+typedef struct _sr_dns_record {
+       int type;
+       char addr[PV_DNS_ADDR];
+} sr_dns_record_t;
+
+typedef struct _sr_dns_item {
+       str name;
+       unsigned int hashid;
+       char hostname[256];
+       int count;
+       int ipv4;
+       int ipv6;
+       sr_dns_record_t r[PV_DNS_RECS];
+       struct _sr_dns_item *next;
+} sr_dns_item_t;
+
+#define SR_DNS_PVIDX   1
+
+typedef struct _dns_pv {
+       sr_dns_item_t *item;
+       int type;
+       int flags;
+       pv_spec_t *pidx;
+       int nidx;
+} dns_pv_t;
+
+
+static sr_dns_item_t *_sr_dns_list = NULL;
+
+/**
+ *
+ */
+sr_dns_item_t *sr_dns_get_item(str *name)
+{
+       sr_dns_item_t *it = NULL;
+       unsigned int hashid = 0;
+
+       hashid =  get_hash1_raw(name->s, name->len);
+
+       it = _sr_dns_list;
+       while(it!=NULL)
+       {
+               if(it->hashid==hashid && it->name.len == name->len
+                               && strncmp(it->name.s, name->s, name->len)==0)
+                       return it;
+               it = it->next;
+       }
+       return NULL;
+}
+
+/**
+ *
+ */
+sr_dns_item_t *sr_dns_add_item(str *name)
+{
+       sr_dns_item_t *it = NULL;
+       unsigned int hashid = 0;
+
+       hashid =  get_hash1_raw(name->s, name->len);
+
+       it = _sr_dns_list;
+       while(it!=NULL)
+       {
+               if(it->hashid==hashid && it->name.len == name->len
+                               && strncmp(it->name.s, name->s, name->len)==0)
+                       return it;
+               it = it->next;
+       }
+       /* add new */
+       it = (sr_dns_item_t*)pkg_malloc(sizeof(sr_dns_item_t));
+       if(it==NULL)
+       {
+               LM_ERR("no more pkg\n");
+               return NULL;
+       }
+       memset(it, 0, sizeof(sr_dns_item_t));
+       it->name.s = (char*)pkg_malloc(name->len+1);
+       if(it->name.s==NULL)
+       {
+               LM_ERR("no more pkg.\n");
+               pkg_free(it);
+               return NULL;
+       }
+       memcpy(it->name.s, name->s, name->len);
+       it->name.s[name->len] = '\0';
+       it->name.len = name->len;
+       it->hashid = hashid;
+       it->next = _sr_dns_list;
+       _sr_dns_list = it;
+       return it;
+}
+
+
+/**
+ *
+ */
+int pv_parse_dns_name(pv_spec_t *sp, str *in)
+{
+       dns_pv_t *dpv=NULL;
+       char *p;
+       str pvc;
+       str pvs;
+       str pvi;
+       int sign;
+
+       if(sp==NULL || in==NULL || in->len<=0)
+               return -1;
+
+       dpv = (dns_pv_t*)pkg_malloc(sizeof(dns_pv_t));
+       if(dpv==NULL)
+               return -1;
+
+       memset(dpv, 0, sizeof(dns_pv_t));
+
+       p = in->s;
+
+       while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
+               p++;
+       if(p>in->s+in->len || *p=='\0')
+               goto error;
+       pvc.s = p;
+       while(p < in->s + in->len)
+       {
+               if(*p=='=' || *p==' ' || *p=='\t' || *p=='\n' || *p=='\r')
+                       break;
+               p++;
+       }
+       if(p>in->s+in->len || *p=='\0')
+               goto error;
+       pvc.len = p - pvc.s;
+       if(*p!='=')
+       {
+               while(p<in->s+in->len && (*p==' ' || *p=='\t' || *p=='\n' || *p=='\r'))
+                       p++;
+               if(p>in->s+in->len || *p=='\0' || *p!='=')
+                       goto error;
+       }
+       p++;
+       if(*p!='>')
+               goto error;
+       p++;
+
+       pvs.len = in->len - (int)(p - in->s);
+       pvs.s = p;
+       pvi.s = 0;
+       pvi.len = 0;
+       if(pvs.s[pvs.len-1]==']') {
+               /* index */
+               p = memchr(pvs.s, '[', pvs.len-1);
+               if(p==NULL) {
+                       goto error;
+               }
+               pvi.s = p + 1;
+               pvi.len = pvs.s + pvs.len - pvi.s;
+               pvs.len = p - pvs.s;
+       }
+       LM_DBG("dns [%.*s] - key [%.*s] index [%.*s]\n", pvc.len, pvc.s,
+                       pvs.len, pvs.s, (pvi.len>0)?pvi.len:0, (pvi.s!=NULL)?pvi.s:0);
+
+       dpv->item = sr_dns_add_item(&pvc);
+       if(dpv->item==NULL)
+               goto error;
+
+       switch(pvs.len)
+       {
+               case 4: 
+                       if(strncmp(pvs.s, "addr", 4)==0)
+                               dpv->type = 0;
+                       else if(strncmp(pvs.s, "type", 4)==0)
+                               dpv->type = 1;
+                       else if(strncmp(pvs.s, "ipv4", 4)==0)
+                               dpv->type = 2;
+                       else if(strncmp(pvs.s, "ipv6", 4)==0)
+                               dpv->type = 3;
+                       else goto error;
+               break;
+               case 5: 
+                       if(strncmp(pvs.s, "count", 5)==0)
+                               dpv->type = 4;
+                       else goto error;
+               break;
+               default:
+                       goto error;
+       }
+
+       if(pvi.len>0)
+       {
+               if(pvi.s[0]==PV_MARKER)
+               {
+                       dpv->pidx = pv_cache_get(&pvi);
+                       if(dpv->pidx==NULL)
+                               goto error;
+                       dpv->flags |= SR_DNS_PVIDX;
+               } else {
+                       sign = 1;
+                       p = pvi.s;
+                       if(*p=='-')
+                       {
+                               sign = -1;
+                               p++;
+                       }
+                       dpv->nidx = 0;
+                       while(p<pvi.s+pvi.len && *p>='0' && *p<='9')
+                       {
+                               dpv->nidx = dpv->nidx * 10 + *p - '0';
+                               p++;
+                       }
+                       if(p!=pvi.s+pvi.len)
+                       {
+                               LM_ERR("invalid index [%.*s]\n", in->len, in->s);
+                               return -1;
+                       }
+                       dpv->nidx *= sign;
+               }
+       }
+       sp->pvp.pvn.u.dname = (void*)dpv;
+       sp->pvp.pvn.type = PV_NAME_OTHER;
+
+       return 0;
+
+error:
+       LM_ERR("error at PV dns name: %.*s\n", in->len, in->s);
+       return -1;
+}
+
+/**
+ *
+ */
+int pv_get_dns(sip_msg_t *msg, pv_param_t *param,
+               pv_value_t *res)
+{
+       dns_pv_t *dpv;
+       pv_value_t val;
+
+       if(msg==NULL || param==NULL)
+               return -1;
+
+       dpv = (dns_pv_t*)param->pvn.u.dname;
+       if(dpv==NULL || dpv->item==NULL)
+               return -1;
+
+       if(dpv->pidx!=NULL)
+       {
+               if(pv_get_spec_value(msg, dpv->pidx, &val)<0
+                               || (!(val.flags&PV_VAL_INT)))
+               {
+                       LM_ERR("failed to evaluate index variable\n");
+                       return pv_get_null(msg, param, res);
+               }
+       } else {
+               val.ri = dpv->nidx;
+       }
+       if(val.ri<0)
+       {
+               if(dpv->item->count+val.ri<0) {
+                       return pv_get_null(msg, param, res);
+               }
+               val.ri = dpv->item->count+val.ri;
+       }
+       if(val.ri>=dpv->item->count) {
+               return pv_get_null(msg, param, res);
+       }
+       switch(dpv->type)
+       {
+               case 0: /* address */
+                       return pv_get_strzval(msg, param, res,
+                                       dpv->item->r[val.ri].addr);
+               case 1: /* type */
+                       return pv_get_sintval(msg, param, res,
+                                       dpv->item->r[val.ri].type);
+               case 2: /* ipv4 */
+                       return pv_get_sintval(msg, param, res,
+                                       dpv->item->ipv4);
+               case 3: /* ipv6 */
+                       return pv_get_sintval(msg, param, res,
+                                       dpv->item->ipv6);
+               case 4: /* count */
+                       return pv_get_sintval(msg, param, res,
+                                       dpv->item->count);
+               default: /* else */
+                       return pv_get_null(msg, param, res);
+       }
+}
+
+/**
+ *
+ */
+int dns_init_pv(char *path)
+{
+       return 0;
+}
+
+/**
+ *
+ */
+void dns_destroy_list(void)
+{
+       return;
+}
+
+/**
+ *
+ */
+void dns_destroy_pv(void)
+{
+       return;
+}
+
+/**
+ *
+ */
+int dns_update_pv(str *hostname, str *name)
+{
+       sr_dns_item_t *dr = NULL;
+       struct addrinfo hints, *res, *p;
+       struct sockaddr_in *ipv4;
+       struct sockaddr_in6 *ipv6;
+       void *addr;
+       int status;
+       int i;
+       
+       if(hostname->len>255)
+       {
+               LM_DBG("target hostname too long (max 255): %s\n", hostname->s);
+               return -2;
+       }
+       
+       dr = sr_dns_get_item(name);
+       if(dr==NULL)
+       {
+               LM_DBG("container not found: %s\n", name->s);
+               return -3;
+       }
+
+       /* reset the counter */
+       dr->count = 0;
+
+       strncpy(dr->hostname, hostname->s, hostname->len);
+       dr->hostname[hostname->len] = '\0';
+       LM_DBG("attempting to resolve: %s\n", dr->hostname);
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC; /* allow any of AF_INET or AF_INET6 */
+       // hints.ai_socktype = SOCK_STREAM;
+       hints.ai_socktype = SOCK_DGRAM;
+
+       if ((status = getaddrinfo(dr->hostname, NULL, &hints, &res)) != 0)
+       {
+        LM_ERR("unable to resolve %s - getaddrinfo: %s\n",
+                               dr->hostname, gai_strerror(status));
+        return -4;
+    }
+
+       i=0;
+       for(p=res; p!=NULL; p=p->ai_next)
+       {
+               if (p->ai_family==AF_INET)
+               {
+                       dr->ipv4 = 1;
+                       dr->r[i].type = 4;
+                       ipv4 = (struct sockaddr_in *)p->ai_addr;
+                       addr = &(ipv4->sin_addr);
+               } else {
+                       dr->ipv6 = 1;
+                       dr->r[i].type = 6;
+                       ipv6 = (struct sockaddr_in6 *)p->ai_addr;
+                       addr = &(ipv6->sin6_addr);
+               }
+               inet_ntop(p->ai_family, addr, dr->r[i].addr,
+                               PV_DNS_ADDR);
+               LM_DBG("#%d - type %d addr: %s (%d)\n", i, dr->r[i].type,
+                               dr->r[i].addr, p->ai_socktype);
+               i++;
+               if(i==PV_DNS_RECS) {
+                       LM_WARN("more than %d addresses for %s - truncating\n",
+                                       PV_DNS_RECS, dr->hostname);
+                       break;
+               }
+    }
+       freeaddrinfo(res);
+
+       dr->count = i;
+
+       LM_DBG("dns PV updated for: %s (%d)\n", dr->hostname, i);
+
+       return 1;
+}
diff --git a/modules/ipops/ipops_pv.h b/modules/ipops/ipops_pv.h
new file mode 100644 (file)
index 0000000..043d891
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2013 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _IPOPS_PV_H_
+#define _IPOPS_PV_H_
+
+#include "../../pvar.h"
+
+int pv_parse_dns_name(pv_spec_t *sp, str *in);
+int pv_get_dns(sip_msg_t *msg, pv_param_t *param,
+               pv_value_t *res);
+
+int dns_init_pv(char *path);
+void dns_destroy_pv(void);
+int dns_update_pv(str *tomatch, str *name);
+
+#endif
+