ipops: added dsn_int_match_ip(hostname, ipaddr)
[sip-router] / modules / ipops / ipops_mod.c
1 /*
2  * ipops module - IPv4 and Ipv6 operations
3  *
4  * Copyright (C) 2011 Iñaki Baz Castillo
5  *
6  * This file is part of SIP Router, a free SIP server.
7  *
8  * SIP Router is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * SIP Router is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * History:
23  * -------
24  *  2011-07-29: Added a function to detect RFC1918 private IPv4 addresses (ibc)
25  *  2011-04-27: Initial version (ibc)
26  */
27 /*!
28  * \file
29  * \brief SIP-router ipops :: Module interface
30  * \ingroup ipops
31  * Copyright (C) 2011 Iñaki Baz Castillo
32  * Module: \ref ipops
33  */
34
35 /*! \defgroup ipops SIP-router ipops Module
36  *
37  * The ipops module provide IPv4 and IPv6 operations.
38  */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netdb.h>
46 #include <arpa/inet.h>
47 #include "../../sr_module.h"
48 #include "../../dprint.h"
49 #include "../../str.h"
50 #include "../../mod_fix.h"
51 #include "../../pvar.h"
52 #include "../../resolve.h"
53 #include "api.h"
54 #include "ip_parser.h"
55 #include "rfc1918_parser.h"
56
57 MODULE_VERSION
58
59
60 /*
61  * Module parameter variables
62  */
63
64
65 /*
66  * Module core functions
67  */
68
69
70 /*
71  * Module internal functions
72  */
73 int _compare_ips(char*, size_t, enum enum_ip_type, char*, size_t, enum enum_ip_type);
74 int _ip_is_in_subnet(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type, int netmask);
75
76
77 /*
78  * Script functions
79  */
80 static int w_is_ip(struct sip_msg*, char*);
81 static int w_is_pure_ip(struct sip_msg*, char*);
82 static int w_is_ipv4(struct sip_msg*, char*);
83 static int w_is_ipv6(struct sip_msg*, char*);
84 static int w_is_ipv6_reference(struct sip_msg*, char*);
85 static int w_ip_type(struct sip_msg*, char*);
86 static int w_compare_ips(struct sip_msg*, char*, char*);
87 static int w_compare_pure_ips(struct sip_msg*, char*, char*);
88 static int w_is_ip_rfc1918(struct sip_msg*, char*);
89 static int w_ip_is_in_subnet(struct sip_msg*, char*, char*);
90 static int w_dns_sys_match_ip(sip_msg_t*, char*, char*);
91 static int w_dns_int_match_ip(sip_msg_t*, char*, char*);
92
93
94 /*
95  * Exported functions
96  */
97 static cmd_export_t cmds[] =
98 {
99   { "is_ip", (cmd_function)w_is_ip, 1, fixup_spve_null, 0,
100   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
101   { "is_pure_ip", (cmd_function)w_is_pure_ip, 1, fixup_spve_null, 0,
102   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
103   { "is_ipv4", (cmd_function)w_is_ipv4, 1, fixup_spve_null, 0,
104   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
105   { "is_ipv6", (cmd_function)w_is_ipv6, 1, fixup_spve_null, 0,
106   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
107   { "is_ipv6_reference", (cmd_function)w_is_ipv6_reference, 1, fixup_spve_null, 0,
108   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
109   { "ip_type", (cmd_function)w_ip_type, 1, fixup_spve_null, 0,
110   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
111   { "compare_ips", (cmd_function)w_compare_ips, 2, fixup_spve_spve, 0,
112   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
113   { "compare_pure_ips", (cmd_function)w_compare_pure_ips, 2, fixup_spve_spve, 0,
114   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
115   { "is_ip_rfc1918", (cmd_function)w_is_ip_rfc1918, 1, fixup_spve_null, 0,
116   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
117   { "is_in_subnet", (cmd_function)w_ip_is_in_subnet, 2, fixup_spve_spve, 0,
118   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
119   { "dns_sys_match_ip", (cmd_function)w_dns_sys_match_ip, 2, fixup_spve_spve, 0,
120   ANY_ROUTE },
121   { "dns_int_match_ip", (cmd_function)w_dns_int_match_ip, 2, fixup_spve_spve, 0,
122   ANY_ROUTE },
123   { "bind_ipops", (cmd_function)bind_ipops, 0, 0, 0, 0},
124   { 0, 0, 0, 0, 0, 0 }
125 };
126
127
128 /*
129  * Module interface
130  */
131 struct module_exports exports = {
132   "ipops",                   /*!< module name */
133   DEFAULT_DLFLAGS,           /*!< dlopen flags */
134   cmds,                      /*!< exported functions */
135   0,                         /*!< exported parameters */
136   0,                         /*!< exported statistics */
137   0,                         /*!< exported MI functions */
138   0,                         /*!< exported pseudo-variables */
139   0,                         /*!< extra processes */
140   0,                         /*!< module initialization function */
141   (response_function) 0,     /*!< response handling function */
142   0,                         /*!< destroy function */
143   0                          /*!< per-child init function */
144 };
145
146
147 /*
148  * Module internal functions
149  */
150
151 /*! \brief Return 1 if both pure IP's are equal, 0 otherwise. */
152 int _compare_ips(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type)
153 {
154   struct in_addr in_addr1, in_addr2;
155   struct in6_addr in6_addr1, in6_addr2;
156   char _ip1[INET6_ADDRSTRLEN], _ip2[INET6_ADDRSTRLEN];
157   
158   // Not same IP type, return false.
159   if (ip1_type != ip2_type)
160     return 0;
161
162   memcpy(_ip1, ip1, len1);
163   _ip1[len1] = '\0';
164   memcpy(_ip2, ip2, len2);
165   _ip2[len2] = '\0';
166
167   switch(ip1_type) {
168     // Comparing IPv4 with IPv4.
169     case(ip_type_ipv4):
170       if (inet_pton(AF_INET, _ip1, &in_addr1) == 0)  return 0;
171       if (inet_pton(AF_INET, _ip2, &in_addr2) == 0)  return 0;
172       if (in_addr1.s_addr == in_addr2.s_addr)
173         return 1;
174       else
175         return 0;
176       break;
177     // Comparing IPv6 with IPv6.
178     case(ip_type_ipv6):
179       if (inet_pton(AF_INET6, _ip1, &in6_addr1) != 1)  return 0;
180       if (inet_pton(AF_INET6, _ip2, &in6_addr2) != 1)  return 0;
181       if (memcmp(in6_addr1.s6_addr, in6_addr2.s6_addr, sizeof(in6_addr1.s6_addr)) == 0)
182         return 1;
183       else
184         return 0;
185       break;
186     default:
187       return 0;
188       break;
189   }
190 }
191
192 /*! \brief Return 1 if IP1 is in the subnet given by IP2 and the netmask, 0 otherwise. */
193 int _ip_is_in_subnet(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type, int netmask)
194 {
195   struct in_addr in_addr1, in_addr2;
196   struct in6_addr in6_addr1, in6_addr2;
197   char _ip1[INET6_ADDRSTRLEN], _ip2[INET6_ADDRSTRLEN];
198   uint32_t ipv4_mask;
199   uint8_t ipv6_mask[16];
200   int i;
201   
202   // Not same IP type, return false.
203   if (ip1_type != ip2_type)
204     return 0;
205
206   memcpy(_ip1, ip1, len1);
207   _ip1[len1] = '\0';
208   memcpy(_ip2, ip2, len2);
209   _ip2[len2] = '\0';
210
211   switch(ip1_type) {
212     // Comparing IPv4 with IPv4.
213     case(ip_type_ipv4):
214       if (inet_pton(AF_INET, _ip1, &in_addr1) == 0)  return 0;
215       if (inet_pton(AF_INET, _ip2, &in_addr2) == 0)  return 0;
216       if (netmask <0 || netmask > 32)  return 0;
217       if (netmask == 32) ipv4_mask = 0xFFFFFFFF;
218       else ipv4_mask = htonl(~(0xFFFFFFFF >> netmask));
219       if ((in_addr1.s_addr & ipv4_mask) == in_addr2.s_addr)
220         return 1;
221       else
222         return 0;
223       break;
224     // Comparing IPv6 with IPv6.
225     case(ip_type_ipv6):
226       if (inet_pton(AF_INET6, _ip1, &in6_addr1) != 1)  return 0;
227       if (inet_pton(AF_INET6, _ip2, &in6_addr2) != 1)  return 0;
228       if (netmask <0 || netmask > 128)  return 0;
229       for (i=0; i<16; i++)
230       {
231         if (netmask > ((i+1)*8)) ipv6_mask[i] = 0xFF;
232         else if (netmask > (i*8))  ipv6_mask[i] = ~(0xFF >> (netmask-(i*8)));
233         else ipv6_mask[i] = 0x00;
234       }
235       for (i=0; i<16; i++)  in6_addr1.s6_addr[i] &= ipv6_mask[i];
236       if (memcmp(in6_addr1.s6_addr, in6_addr2.s6_addr, sizeof(in6_addr1.s6_addr)) == 0)
237         return 1;
238       else
239         return 0;
240       break;
241     default:
242       return 0;
243       break;
244   }
245 }
246
247
248
249 /*
250  * Script functions
251  */
252
253 /*! \brief Return true if the given argument (string or pv) is a valid IPv4, IPv6 or IPv6 reference. */
254 static int w_is_ip(struct sip_msg* _msg, char* _s)
255 {
256   str string;
257   
258   if (_s == NULL) {
259     LM_ERR("bad parameter\n");
260     return -2;
261   }
262   
263   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
264   {
265     LM_ERR("cannot print the format for string\n");
266     return -3;
267   }
268   
269   if (ip_parser_execute(string.s, string.len) != ip_type_error)
270     return 1;
271   else
272     return -1;
273 }
274
275
276 /*! \brief Return true if the given argument (string or pv) is a valid IPv4 or IPv6. */
277 static int w_is_pure_ip(struct sip_msg* _msg, char* _s)
278 {
279   str string;
280   
281   if (_s == NULL) {
282     LM_ERR("bad parameter\n");
283     return -2;
284   }
285   
286   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
287   {
288     LM_ERR("cannot print the format for string\n");
289     return -3;
290   }
291
292   switch(ip_parser_execute(string.s, string.len)) {
293     case(ip_type_ipv4):
294       return 1;
295       break;
296     case(ip_type_ipv6):
297       return 1;
298       break;
299     default:
300       return -1;
301       break;
302   }
303 }
304
305
306 /*! \brief Return true if the given argument (string or pv) is a valid IPv4. */
307 static int w_is_ipv4(struct sip_msg* _msg, char* _s)
308 {
309   str string;
310   
311   if (_s == NULL) {
312     LM_ERR("bad parameter\n");
313     return -2;
314   }
315   
316   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
317   {
318     LM_ERR("cannot print the format for string\n");
319     return -3;
320   }
321
322   if (ip_parser_execute(string.s, string.len) == ip_type_ipv4)
323     return 1;
324   else
325     return -1;
326 }
327
328
329 /*! \brief Return true if the given argument (string or pv) is a valid IPv6. */
330 static int w_is_ipv6(struct sip_msg* _msg, char* _s)
331 {
332   str string;
333   
334   if (_s == NULL) {
335     LM_ERR("bad parameter\n");
336     return -2;
337   }
338   
339   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
340   {
341     LM_ERR("cannot print the format for string\n");
342     return -3;
343   }
344   
345   if (ip_parser_execute(string.s, string.len) == ip_type_ipv6)
346     return 1;
347   else
348     return -1;
349 }
350
351
352 /*! \brief Return true if the given argument (string or pv) is a valid IPv6 reference. */
353 static int w_is_ipv6_reference(struct sip_msg* _msg, char* _s)
354 {
355   str string;
356   
357   if (_s == NULL) {
358     LM_ERR("bad parameter\n");
359     return -2;
360   }
361   
362   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
363   {
364     LM_ERR("cannot print the format for string\n");
365     return -3;
366   }
367   
368   if (ip_parser_execute(string.s, string.len) == ip_type_ipv6_reference)
369     return 1;
370   else
371     return -1;
372 }
373
374
375 /*! \brief Return the IP type of the given argument (string or pv): 1 = IPv4, 2 = IPv6, 3 = IPv6 refenrece, -1 = invalid IP. */
376 static int w_ip_type(struct sip_msg* _msg, char* _s)
377 {
378   str string;
379   
380   if (_s == NULL) {
381     LM_ERR("bad parameter\n");
382     return -2;
383   }
384   
385   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
386   {
387     LM_ERR("cannot print the format for string\n");
388     return -3;
389   }
390   
391   switch (ip_parser_execute(string.s, string.len)) {
392     case(ip_type_ipv4):
393       return 1;
394       break;
395     case(ip_type_ipv6):
396       return 2;
397       break;
398     case(ip_type_ipv6_reference):
399       return 3;
400       break;
401     default:
402       return -1;
403       break;
404   }
405 }
406
407
408 /*! \brief Return true if both IP's (string or pv) are equal. This function also allows comparing an IPv6 with an IPv6 reference. */
409 static int w_compare_ips(struct sip_msg* _msg, char* _s1, char* _s2)
410 {
411   str string1, string2;
412   enum enum_ip_type ip1_type, ip2_type;
413   
414   if (_s1 == NULL || _s2 == NULL ) {
415     LM_ERR("bad parameters\n");
416     return -2;
417   }
418   
419   if (fixup_get_svalue(_msg, (gparam_p)_s1, &string1))
420   {
421     LM_ERR("cannot print the format for first string\n");
422     return -3;
423   }
424
425   if (fixup_get_svalue(_msg, (gparam_p)_s2, &string2))
426   {
427     LM_ERR("cannot print the format for second string\n");
428     return -3;
429   }
430
431   switch(ip1_type = ip_parser_execute(string1.s, string1.len)) {
432     case(ip_type_error):
433       return -1;
434       break;
435     case(ip_type_ipv6_reference):
436       string1.s += 1;
437       string1.len -= 2;
438       ip1_type = ip_type_ipv6;
439       break;
440     default:
441       break;
442   }
443   switch(ip2_type = ip_parser_execute(string2.s, string2.len)) {
444     case(ip_type_error):
445       return -1;
446       break;
447     case(ip_type_ipv6_reference):
448       string2.s += 1;
449       string2.len -= 2;
450       ip2_type = ip_type_ipv6;
451       break;
452     default:
453       break;
454   }
455
456   if (_compare_ips(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type))
457     return 1;
458   else
459     return -1;
460 }
461
462
463 /*! \brief Return true if both pure IP's (string or pv) are equal. IPv6 references not allowed. */
464 static int w_compare_pure_ips(struct sip_msg* _msg, char* _s1, char* _s2)
465 {
466   str string1, string2;
467   enum enum_ip_type ip1_type, ip2_type;
468   
469   if (_s1 == NULL || _s2 == NULL ) {
470     LM_ERR("bad parameters\n");
471     return -2;
472   }
473   
474   if (fixup_get_svalue(_msg, (gparam_p)_s1, &string1))
475   {
476     LM_ERR("cannot print the format for first string\n");
477     return -3;
478   }
479   
480   if (fixup_get_svalue(_msg, (gparam_p)_s2, &string2))
481   {
482     LM_ERR("cannot print the format for second string\n");
483     return -3;
484   }
485
486   switch(ip1_type = ip_parser_execute(string1.s, string1.len)) {
487     case(ip_type_error):
488       return -1;
489       break;
490     case(ip_type_ipv6_reference):
491       return -1;
492       break;
493     default:
494       break;
495   }
496   switch(ip2_type = ip_parser_execute(string2.s, string2.len)) {
497     case(ip_type_error):
498       return -1;
499       break;
500     case(ip_type_ipv6_reference):
501       return -1;
502       break;
503     default:
504       break;
505   }
506   
507   if (_compare_ips(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type))
508     return 1;
509   else
510     return -1;
511 }
512
513
514 /*! \brief Return true if the first IP (string or pv) is within the subnet defined by the second IP in CIDR notation. IPv6 references not allowed. */
515 static int w_ip_is_in_subnet(struct sip_msg* _msg, char* _s1, char* _s2)
516 {
517   str string1, string2;
518   enum enum_ip_type ip1_type, ip2_type;
519   char *cidr_pos = NULL;
520   int netmask = 0;
521   
522   if (_s1 == NULL || _s2 == NULL ) {
523     LM_ERR("bad parameters\n");
524     return -2;
525   }
526   
527   if (fixup_get_svalue(_msg, (gparam_p)_s1, &string1))
528   {
529     LM_ERR("cannot print the format for first string\n");
530     return -3;
531   }
532   
533   if (fixup_get_svalue(_msg, (gparam_p)_s2, &string2))
534   {
535     LM_ERR("cannot print the format for second string\n");
536     return -3;
537   }
538
539   switch(ip1_type = ip_parser_execute(string1.s, string1.len)) {
540     case(ip_type_error):
541       return -1;
542       break;
543     case(ip_type_ipv6_reference):
544       return -1;
545       break;
546     default:
547       break;
548   }
549   cidr_pos = string2.s + string2.len - 1;
550   while (cidr_pos > string2.s)
551   {
552     if (*cidr_pos == '/')
553     {
554       string2.len = (cidr_pos - string2.s);
555       netmask = atoi(cidr_pos+1);
556       break;
557     }
558     cidr_pos--;
559   }
560   switch(ip2_type = ip_parser_execute(string2.s, string2.len)) {
561     case(ip_type_error):
562       return -1;
563       break;
564     case(ip_type_ipv6_reference):
565       return -1;
566       break;
567     default:
568       break;
569   }
570
571   if (netmask == 0)
572   {
573     if (_compare_ips(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type))
574       return 1;
575     else
576       return -1;
577   }
578   else
579   {
580     if (_ip_is_in_subnet(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type, netmask))
581       return 1;
582     else
583       return -1;
584   }
585 }
586
587
588 /*! \brief Return true if the given argument (string or pv) is a valid RFC 1918 IPv4 (private address). */
589 static int w_is_ip_rfc1918(struct sip_msg* _msg, char* _s)
590 {
591   str string;
592   
593   if (_s == NULL) {
594     LM_ERR("bad parameter\n");
595     return -2;
596   }
597   
598   if (fixup_get_svalue(_msg, (gparam_p)_s, &string))
599   {
600     LM_ERR("cannot print the format for string\n");
601     return -3;
602   }
603   
604   if (rfc1918_parser_execute(string.s, string.len) == 1)
605     return 1;
606   else
607     return -1;
608 }
609
610 static inline ip_addr_t *strtoipX(str *ips)
611 {
612         /* try to figure out INET class */
613         if(ips->s[0] == '[' || memchr(ips->s, ':', ips->len)!=NULL)
614         {
615                 /* IPv6 */
616                 return str2ip6(ips);
617         } else {
618                 /* IPv4 */
619                 return str2ip(ips);
620         }
621 }
622
623 static int w_dns_sys_match_ip(sip_msg_t *msg, char *hnp, char *ipp)
624 {
625         struct addrinfo hints, *res, *p;
626         int status;
627         ip_addr_t *ipa;
628         void *addr;
629         str hns;
630         str ips;
631         struct sockaddr_in *ipv4;
632         struct sockaddr_in6 *ipv6;
633
634         if (fixup_get_svalue(msg, (gparam_p)hnp, &hns))
635         {
636                 LM_ERR("cannot evaluate hostname parameter\n");
637                 return -2;
638         }
639
640         if (fixup_get_svalue(msg, (gparam_p)ipp, &ips))
641         {
642                 LM_ERR("cannot evaluate ip address parameter\n");
643                 return -2;
644         }
645
646         ipa = strtoipX(&ips);
647         if(ipa==NULL)
648         {
649                 LM_ERR("invalid ip address: %.*s\n", ips.len, ips.s);
650                 return -3;
651         }
652
653         memset(&hints, 0, sizeof(hints));
654         hints.ai_family = AF_UNSPEC; /* allow any of AF_INET or AF_INET6 */
655
656         if ((status = getaddrinfo(hns.s, NULL, &hints, &res)) != 0)
657         {
658         LM_ERR("getaddrinfo: %s\n", gai_strerror(status));
659         return -4;
660     }
661
662         for(p = res;p != NULL; p = p->ai_next)
663         {
664                 if(p->ai_family==ipa->af)
665                 {
666                         if (p->ai_family==AF_INET)
667                         {
668                                 ipv4 = (struct sockaddr_in *)p->ai_addr;
669                                 addr = &(ipv4->sin_addr);
670                         } else {
671                                 ipv6 = (struct sockaddr_in6 *)p->ai_addr;
672                                 addr = &(ipv6->sin6_addr);
673                         }
674                         if(memcmp(ipa->u.addr, addr, ipa->len)==0)
675                         {
676                                 /* matched IP */
677                                 freeaddrinfo(res);
678                                 return 1;
679                         }
680                 }
681     }
682         freeaddrinfo(res);
683
684         return -1;
685 }
686
687 static int w_dns_int_match_ip(sip_msg_t *msg, char *hnp, char *ipp)
688 {
689         ip_addr_t *ipa;
690         str hns;
691         str ips;
692         struct hostent* he;
693         char ** h;
694         int ret;
695
696         if (fixup_get_svalue(msg, (gparam_p)hnp, &hns))
697         {
698                 LM_ERR("cannot evaluate hostname parameter\n");
699                 return -2;
700         }
701
702         if (fixup_get_svalue(msg, (gparam_p)ipp, &ips))
703         {
704                 LM_ERR("cannot evaluate ip address parameter\n");
705                 return -2;
706         }
707
708         ipa = strtoipX(&ips);
709         if(ipa==NULL)
710         {
711                 LM_ERR("invalid ip address: %.*s\n", ips.len, ips.s);
712                 return -3;
713         }
714
715         he=resolvehost(hns.s);
716         if (he==0) {
717                 DBG("could not resolve %s\n", hns.s);
718                 return -4;
719         }
720         ret = 0;
721         if (he->h_addrtype==ipa->af)
722         {
723                 for(h=he->h_addr_list; (*h); h++)
724                 {
725                         if(memcmp(ipa->u.addr, *h, ipa->len)==0)
726                         {
727                                 /* match */
728                                 return 1;
729                         }
730                 }
731         }
732         /* no match */
733         return -1;
734 }