1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 /** \file wzd_ip.c
28  * \brief IP address related routines
29  */
30 
31 #include "wzd_all.h"
32 
33 #ifndef WZD_USE_PCH
34 
35 #ifdef WIN32
36 #include <winsock2.h>
37 #include <ws2tcpip.h>
38 #else
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>  /* struct in_addr (wzd_misc.h) */
43 
44 #include <netdb.h>
45 #endif
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/stat.h>
51 #include <ctype.h>
52 
53 #include "wzd_structs.h"
54 
55 #include "wzd_configfile.h"
56 #include "wzd_group.h"
57 #include "wzd_ip.h"
58 #include "wzd_log.h"
59 #include "wzd_misc.h"
60 #include "wzd_socket.h"
61 #include "wzd_user.h"
62 
63 #include "wzd_debug.h"
64 
65 #endif /* WZD_USE_PCH */
66 
67 #define MAX_NUMERIC_IP_LEN 64
68 
69 struct _wzd_ip_t {
70   net_family_t family;
71 
72   enum host_type_t type;
73   unsigned int netmask;
74 
75   char raw[MAX_NUMERIC_IP_LEN];
76 };
77 
78 
79 static int string_is_hostname(const char * s);
80 static int string_is_ipv4(const char * s);
81 static int string_is_ipv6(const char * s);
82 
83 
84 /** \brief Allocate and initialize a new \a wzd_ip_t struct
85  */
ip_create(void)86 wzd_ip_t * ip_create(void)
87 {
88   wzd_ip_t * ip;
89 
90   ip = wzd_malloc(sizeof(*ip));
91   memset(ip,0,sizeof(*ip));
92 
93   return ip;
94 }
95 
96 /** \brief Frees a \wzd_ip_t struct
97  */
ip_free(wzd_ip_t * ip)98 void ip_free(wzd_ip_t * ip)
99 {
100   wzd_free(ip);
101 }
102 
103 
104 /** \brief IP comparison
105  *
106  * ip1 must be a numeric ip
107  * ip2 can be composed of wildcards
108  *
109  * \note
110  * The * wildcard will stop at the first match:
111  *   1*0 will match 15.0 whereas 1*0 will not match 10.0
112  *
113  * \return 1 if identical
114  */
ip_compare(const char * ip,const char * pattern)115 int ip_compare(const char * ip, const char * pattern)
116 {
117   char buffer1[256], buffer2[256];
118   const char *ptr;
119   int has_wildcards1=0, has_wildcards2=0;
120 #ifndef IPV6_SUPPORT
121   struct hostent * host;
122 #endif
123 
124   if (!ip || !pattern) return 0;
125 
126   /* simple case */
127   if (strcmp(ip,pattern)==0) return 1;
128 
129   has_wildcards1 = ( strpbrk(ip,"*?") != NULL );
130   has_wildcards2 = ( strpbrk(pattern,"*?") != NULL );
131 
132 #ifndef IPV6_SUPPORT
133   if (!has_wildcards1 && !has_wildcards2) { /* no wildcards */
134     if (socket_getipbyname(ip, buffer1, sizeof(buffer1))) return 0;
135 
136     if (socket_getipbyname(pattern, buffer2, sizeof(buffer2))) return 0;
137 
138     if (memcmp(buffer1,buffer2,4)==0) /** and for IPv6 ?! */
139       return 1;
140 
141     /* other aliases for host ?! */
142 
143     return 0;
144   } /* no wildcards */
145 
146   if (has_wildcards1 && has_wildcards2) { /* wildcards in both strings ... I don't know what to do */
147     return 0;
148   }
149 
150   if (has_wildcards1 && !has_wildcards2) { /* swap ip to have only wildcards in ip2 */
151     ptr = ip;
152     pattern = ip;
153     ip = ptr;
154   }
155 
156   /* here, only ip2 contains wildcards */
157   if (socket_getipbyname(ip, buffer1, sizeof(buffer1))) return 0;
158 
159   /* try direct match: 127.0.0.1 vs 127.0.0.* */
160   if (my_str_compare(ip,pattern)==1)
161     return 1;
162 
163   /* try reverse lookup */
164   host = gethostbyaddr(buffer1,4,AF_INET); /** \todo will not work with IPv6 */
165   if (!host) return 0;
166   if (my_str_compare(host->h_name,pattern)==1)
167     return 1;
168 #else /* IPV6_SUPPORT */
169   {
170     struct addrinfo aiHint;
171     struct addrinfo * aiList = NULL, * aiListPattern = NULL;
172     int retval;
173 
174     if (strncmp(ip,"::ffff:",strlen("::ffff:"))==0)
175       ip += strlen("::ffff:");
176 
177     memset(&aiHint,0,sizeof(struct addrinfo));
178     aiHint.ai_family = AF_UNSPEC;
179     aiHint.ai_socktype = SOCK_STREAM;
180     aiHint.ai_protocol = IPPROTO_TCP;
181     memset(buffer1,0,sizeof(struct in6_addr));
182     memset(buffer2,0,sizeof(struct in6_addr));
183 
184     if (!has_wildcards1 && !has_wildcards2) { /* no wildcards */
185       retval = getaddrinfo(ip, NULL, &aiHint, &aiList);
186       if (retval) return 0;
187       memcpy(buffer1, aiList->ai_addr, aiList->ai_addrlen);
188 
189       freeaddrinfo(aiList);
190 
191       retval = getaddrinfo(pattern, NULL, &aiHint, &aiListPattern);
192       if (retval) return 0;
193       memcpy(buffer2, aiListPattern->ai_addr, aiListPattern->ai_addrlen);
194 
195       freeaddrinfo(aiListPattern);
196 
197       if (memcmp(buffer1,buffer2,sizeof(struct in6_addr))==0)
198         return 1;
199 
200       /* other aliases for host ?! */
201 
202       return 0;
203     } /* no wildcards */
204 
205     if (has_wildcards1 && has_wildcards2) { /* wildcards in both strings ... I don't know what to do */
206       return 0;
207     }
208 
209     if (has_wildcards1 && !has_wildcards2) { /* swap ip to have only wildcards in ip2 */
210       ptr = ip;
211       pattern = ip;
212       ip = ptr;
213     }
214 
215     /* here, only ip2 contains wildcards */
216     retval = getaddrinfo(ip, NULL, &aiHint, &aiList);
217     if (retval) return 0;
218     memcpy(buffer1, aiList->ai_addr, aiList->ai_addrlen);
219 
220     freeaddrinfo(aiList);
221 
222     /* try direct match: 127.0.0.1 vs 127.0.0.* */
223     if (my_str_compare(ip,pattern)==1)
224       return 1;
225 
226     /* try reverse lookup */
227     aiHint.ai_flags = AI_CANONNAME;
228     retval = getaddrinfo(ip, NULL, &aiHint, &aiList);
229     if (retval) return 0;
230     wzd_strncpy(buffer1, aiList->ai_canonname, sizeof(buffer1));
231 
232     freeaddrinfo(aiList);
233 
234     if (my_str_compare(buffer1,pattern)==1)
235       return 1;
236   }
237 #endif /* IPV6_SUPPORT */
238 
239   return 0;
240 }
241 
242 
243 /** \brief Add a new ip to be checked when user logs in
244  */
ip_add_check(struct wzd_ip_list_t ** list,const char * newip,int is_allowed)245 int ip_add_check(struct wzd_ip_list_t **list, const char *newip, int is_allowed)
246 {
247   struct wzd_ip_list_t * new_ip_t, *insert_point;
248 
249   WZD_ASSERT( list != NULL );
250 
251   if (strlen(newip) < 1) return -1;
252   if (strlen(newip) >= MAX_IP_LENGTH) return -1; /* upper limit for an hostname */
253 
254   new_ip_t = malloc(sizeof(*new_ip_t));
255   new_ip_t->regexp = wzd_strndup(newip,MAX_IP_LENGTH);
256   new_ip_t->is_allowed = (is_allowed) ? 1 : 0;
257   new_ip_t->next_ip = NULL;
258 
259   /* tail insertion, be aware that order is important */
260   insert_point = *list;
261   if (insert_point == NULL) {
262     *list = new_ip_t;
263   } else {
264     /** \note using a circular list would be faster here */
265     while (insert_point->next_ip != NULL)
266       insert_point = insert_point->next_ip;
267 
268     insert_point->next_ip = new_ip_t;
269   }
270 
271   return 0;
272 }
273 
274 /** \brief Check if ip is allowed by list.
275  *
276  * \returns 1 if allowed, 0 if denied, -1 on error or if not found
277  */
ip_list_check(struct wzd_ip_list_t * list,const char * ip)278 int ip_list_check(struct wzd_ip_list_t *list, const char *ip)
279 {
280   struct wzd_ip_list_t * current_ip;
281   char * ptr_test;
282 
283   current_ip = list;
284   while (current_ip) {
285     ptr_test = current_ip->regexp;
286     if (*ptr_test == '\0') return -1; /* ip has length 0 ! */
287 
288     if (ip_compare(ip,ptr_test)==1) return current_ip->is_allowed;
289 
290     current_ip = current_ip->next_ip;
291   } /* while current_ip */
292 
293   return -1;
294 }
295 
296 /** \brief Check if ip is allowed by list, comparing \a ident if present
297  *
298  * \returns 1 if allowed, 0 if denied, -1 on error or if not found
299  */
ip_list_check_ident(struct wzd_ip_list_t * list,const char * ip,const char * ident)300 int ip_list_check_ident(struct wzd_ip_list_t *list, const char *ip, const char * ident)
301 {
302   struct wzd_ip_list_t * current_ip;
303   char buffer[1024];
304   const char * ptr;
305   const char * ident_ref, * ip_ref;
306 
307   for (current_ip = list; current_ip != NULL; current_ip = current_ip->next_ip) {
308     ip_ref = current_ip->regexp;
309 
310     if ( (ptr = strchr(current_ip->regexp,'@'))!=NULL ) {
311       /* split regexp into ident_ref and ip_ref */
312       ip_ref = ptr+1;
313       strncpy(buffer,current_ip->regexp,(ptr-current_ip->regexp));
314       buffer[ptr-current_ip->regexp] = '\0';
315       ident_ref = buffer;
316       /* Check ident and exit if different */
317       if (ident == NULL) {
318         /* if ident is NULL, we can still accept it if ident_ref is the wildcard * */
319         if (strcmp(ident_ref,"*")!=0) continue;
320       } else {
321         if (my_str_compare(ident,ident_ref)!=1) continue;
322       }
323     }
324 
325     /* if the ident check is ok, check the ip */
326     if (ip_compare(ip,ip_ref)==1) return current_ip->is_allowed;
327   }
328 
329   return -1;
330 }
331 
332 /** \brief Remove \a ip from list
333  * \return 0 if ok, -1 if not found
334  */
ip_remove(struct wzd_ip_list_t ** list,const char * ip)335 int ip_remove(struct wzd_ip_list_t ** list, const char * ip)
336 {
337   struct wzd_ip_list_t * current_ip, * free_ip;
338 
339   current_ip = *list;
340   if (current_ip == NULL) return -1;
341 
342   /* first ? */
343   if (strcmp(current_ip->regexp, ip)==0) {
344     *list = (*list)->next_ip;
345     wzd_free(current_ip->regexp);
346     wzd_free(current_ip);
347     return 0;
348   }
349 
350   while (current_ip->next_ip && current_ip->next_ip->regexp) {
351     if (strcmp(current_ip->next_ip->regexp,ip)==0) {
352       free_ip = current_ip->next_ip;
353       current_ip->next_ip = free_ip->next_ip;
354       wzd_free(free_ip->regexp);
355       wzd_free(free_ip);
356       return 0;
357     }
358     current_ip = current_ip->next_ip;
359   }
360 
361   return -1;
362 }
363 
ip_inlist(struct wzd_ip_list_t * list,const char * ip)364 int ip_inlist(struct wzd_ip_list_t *list, const char *ip)
365 {
366   struct wzd_ip_list_t * current_ip;
367   const char * ptr_ip;
368   char * ptr_test;
369 
370   current_ip = list;
371   while (current_ip) {
372     ptr_ip = ip;
373     ptr_test = current_ip->regexp;
374     if (*ptr_test == '\0') return 0; /* ip has length 0 ! */
375 
376     if (ip_compare(ptr_ip,ptr_test)==1) return 1;
377 
378     current_ip = current_ip->next_ip;
379   } /* while current_ip */
380 
381   return 0;
382 }
383 
ip_list_free(struct wzd_ip_list_t * list)384 void ip_list_free(struct wzd_ip_list_t *list)
385 {
386   struct wzd_ip_list_t * current, *next;
387 
388   if (!list) return;
389   current = list;
390 
391   while (current) {
392     next = current->next_ip;
393 
394     free(current->regexp);
395     free(current);
396 
397     current = next;
398   }
399 }
400 
401 
hostnametoip(const char * hostname,char ** ip,size_t * length,net_family_t * family)402 int hostnametoip(const char *hostname, char **ip, size_t *length, net_family_t *family)
403 {
404 #if defined(HAVE_GETADDRINFO)
405   {
406     struct addrinfo * result = NULL;
407     int error;
408     const char * ptr;
409     char ip_buf[128];
410     struct sockaddr_in * addr4;
411     struct sockaddr_in6 * addr6;
412 
413     error = getaddrinfo(hostname,NULL,NULL,&result);
414     if (error) {
415       out_log(LEVEL_NORMAL,"Error using getaddrinfo: %s\n",gai_strerror(error));
416       *ip = NULL;
417       return -1;
418     }
419     out_err(LEVEL_FLOOD,"Family: %d\n",result->ai_family);
420     switch (result->ai_family) {
421       case PF_INET:
422         if (family) *family = WZD_INET4;
423         addr4 = (struct sockaddr_in*)result->ai_addr;
424         ptr = inet_ntop(AF_INET,(void*)&addr4->sin_addr,ip_buf,sizeof(ip_buf));
425         break;
426       case PF_INET6:
427         if (family) *family = WZD_INET6;
428         addr6 = (struct sockaddr_in6*)result->ai_addr;
429         ptr = inet_ntop(AF_INET6,addr6->sin6_addr.s6_addr,ip_buf,sizeof(ip_buf));
430         break;
431       default:
432         out_log(LEVEL_NORMAL,"getaddrinfo: unsupported family %d\n",result->ai_family);
433         freeaddrinfo(result);
434         return -1;
435     }
436     if (ptr == NULL) {
437       out_log(LEVEL_NORMAL,"Error converting address with inet_ntop\n");
438       freeaddrinfo(result);
439       return -1;
440     }
441     out_err(LEVEL_FLOOD,"Address: %s\n",ip_buf);
442     if (ip) *ip = wzd_strdup(ip_buf);
443     if (length) *length = strlen(ip_buf);
444 
445     freeaddrinfo(result);
446     return 0;
447   }
448 #else
449   {
450     struct hostent * hent;
451     const char * ptr;
452     char ip_buf[128];
453 
454     /** \bug FIXME gethostbyname is _not_ thread-safe */
455     hent = gethostbyname(hostname);
456     if (hent == NULL) {
457       /* TODO: fix this. it currently breaks win32 compiles */
458 #ifndef WIN32
459       out_log(LEVEL_NORMAL,"Error using gethostbyname: %s\n",hstrerror(h_errno));
460 #endif
461       return -1;
462     }
463 
464     switch (hent->h_addrtype) {
465       case AF_INET:
466         if (family) *family = WZD_INET4;
467         ptr = inet_ntop(AF_INET,hent->h_addr,ip_buf,sizeof(ip_buf));
468         break;
469       case AF_INET6:
470         if (family) *family = WZD_INET6;
471         ptr = inet_ntop(AF_INET6,hent->h_addr,ip_buf,sizeof(ip_buf));
472         break;
473       default:
474         out_log(LEVEL_NORMAL,"gethostbyname: unsupported family %d\n",hent->h_addrtype);
475         return -1;
476     }
477 
478     if (ptr == NULL) {
479       out_log(LEVEL_NORMAL,"Error converting address with inet_ntop\n");
480       return -1;
481     }
482     out_err(LEVEL_FLOOD,"Address: %s\n",ip_buf);
483     if (ip) *ip = wzd_strdup(ip_buf);
484     if (length) *length = strlen(ip_buf);
485 
486     return 0;
487   }
488 #endif
489   return -1;
490 }
491 
iptohostname(const char * ip,net_family_t family,char ** hostname,size_t * length)492 int iptohostname(const char *ip, net_family_t family, char **hostname, size_t *length)
493 {
494 #if defined(HAVE_GETADDRINFO) && defined(HAVE_GETNAMEINFO)
495   {
496     char tmphost[NI_MAXHOST];
497     struct addrinfo * result = NULL;
498     struct addrinfo hints;
499     int error;
500     int ai_family;
501 
502     if (hostname) *hostname = NULL;
503 
504     switch (family) {
505       case WZD_INET_NONE:
506         ai_family = AF_UNSPEC;
507         break;
508       case WZD_INET4:
509         ai_family = AF_INET;
510         break;
511       case WZD_INET6:
512         ai_family = AF_INET6;
513         break;
514       default:
515         out_log(LEVEL_NORMAL,"iptohostname: unsupported family %d\n",family);
516         return -1;
517     }
518 
519     memset(&hints,0,sizeof(hints));
520     hints.ai_family = ai_family;
521     hints.ai_socktype = SOCK_STREAM;
522     hints.ai_protocol = IPPROTO_TCP;
523     hints.ai_flags = AI_CANONNAME;
524 
525     error = getaddrinfo(ip, NULL, &hints, &result);
526     if (error) {
527       out_log(LEVEL_NORMAL,"Error using getaddrinfo: %s\n",gai_strerror(error));
528       return -1;
529     }
530 
531     error = getnameinfo (result->ai_addr, result->ai_addrlen, tmphost, sizeof(tmphost), NULL, 0, 0);
532     if (error) {
533       out_log(LEVEL_NORMAL,"Error using getnameinfo: %s\n",gai_strerror(error));
534       freeaddrinfo(result);
535       return -1;
536     }
537 
538     out_err(LEVEL_FLOOD,"AddressToIP: %s\n",tmphost);
539     if (hostname) *hostname = wzd_strdup(tmphost);
540     if (length) *length = strlen(tmphost);
541 
542     freeaddrinfo(result);
543     return 0;
544   }
545 #else
546   {
547     int ai_family;
548     struct hostent * hent;
549     char ip_buffer[128];
550 
551     if (hostname) *hostname = NULL;
552 
553     switch (family) {
554       case WZD_INET_NONE:
555         /* guess family */
556         if (strchr(ip,':')!=NULL) {
557           family = WZD_INET6;
558           ai_family = AF_INET6;
559         } else {
560           family = WZD_INET4;
561           ai_family = AF_INET;
562         }
563         break;
564       case WZD_INET4:
565         ai_family = AF_INET;
566         break;
567       case WZD_INET6:
568         ai_family = AF_INET6;
569         break;
570       default:
571         out_log(LEVEL_NORMAL,"iptohostname: unsupported family %d\n",family);
572         return -1;
573     }
574 
575     memset(ip_buffer,0,sizeof(ip_buffer));
576     /* convert ip (string) to ip (numeric form) */
577     hent = gethostbyname(ip);
578     if (hent == NULL) {
579       /* TODO: fix this it does not compile on win32 */
580 #ifndef WIN32
581       out_log(LEVEL_NORMAL,"Error using gethostbyname: %s\n",hstrerror(h_errno));
582 #endif
583       return -1;
584     }
585     memcpy(ip_buffer,hent->h_addr,(family==WZD_INET6) ? 16 : 4);
586 
587 
588     hent = gethostbyaddr(ip_buffer,(family==WZD_INET6)?16:4,ai_family);
589 
590     if (hent == NULL) {
591       /* TODO: fix this it does not compile on win32 */
592 #ifndef WIN32
593       out_log(LEVEL_NORMAL,"Error using gethostbyaddr: %s\n",hstrerror(h_errno));
594 #endif
595       return -1;
596     }
597 
598     out_err(LEVEL_FLOOD,"AddressToIP: %s\n",hent->h_name);
599     if (hostname) *hostname = wzd_strdup(hent->h_name);
600     if (length) *length = strlen(hent->h_name);
601 
602     return 0;
603   }
604 #endif
605   return -1;
606 }
607 
608 /** \brief Test if remote peer is known as a BNC
609  *
610  * \return 1 if peer is a BNC
611  */
ip_is_bnc(const char * remote,wzd_config_t * config)612 int ip_is_bnc(const char * remote, wzd_config_t * config)
613 {
614   wzd_string_t ** bnc_list;
615   wzd_string_t * bnc;
616   int errcode;
617   int i;
618 
619   WZD_ASSERT(config != NULL);
620   WZD_ASSERT(remote != NULL);
621 
622   if (!config || !remote) return 0;
623 
624   bnc_list = config_get_string_list (config->cfg_file, "GLOBAL", "bnc_list", &errcode);
625   if (!bnc_list) return 0;
626 
627   for (i=0; bnc_list[i] != NULL; i++) {
628     bnc = bnc_list[i];
629     if (ip_compare(remote,str_tochar(bnc)) == 1) { /* found */
630       str_deallocate_array(bnc_list);
631       return 1;
632     }
633   }
634 
635   str_deallocate_array(bnc_list);
636   return 0;
637 }
638 
639 /** \brief Return our own ip
640  *
641  * \a buffer must be at least 16 bytes long
642  */
getmyip(int sock,net_family_t family,unsigned char * buffer)643 unsigned char * getmyip(int sock, net_family_t family, unsigned char * buffer)
644 {
645   struct sockaddr_in sa;
646   unsigned int size;
647 
648 #if defined(IPV6_SUPPORT)
649   if (family == WZD_INET6) {
650     struct sockaddr_in6 sa6;
651 
652     size = sizeof(struct sockaddr_in6);
653     memset(buffer,0,16);
654     if (getsockname(sock,(struct sockaddr *)&sa6,&size)!=-1)
655     {
656       memcpy(buffer,&sa6.sin6_addr,16);
657     } else { /* failed, using localhost */
658       out_log(LEVEL_CRITICAL,"getmyip: could not get my own ip !\n");
659       return NULL;
660     }
661 
662     return buffer;
663   }
664 #endif /* IPV6_SUPPORT */
665   size = sizeof(struct sockaddr_in);
666   memset(buffer,0,16);
667   if (getsockname(sock,(struct sockaddr *)&sa,&size)!=-1)
668   {
669     memcpy(buffer,&sa.sin_addr,4);
670   } else { /* failed, using localhost */
671     out_log(LEVEL_CRITICAL,"getmyip: could not get my own ip !\n");
672     return NULL;
673   }
674 
675   return buffer;
676 }
677 
678 /** \brief Parse string and return host object or NULL
679  */
ip_parse_host(const char * host)680 wzd_ip_t * ip_parse_host(const char *host)
681 {
682   wzd_ip_t * ip = NULL;
683   char * ptr;
684   char * slash;
685   char * text = NULL, * start = NULL;
686   enum host_type_t type = HT_UNKNOWN;
687   unsigned int netmask = 0;
688 
689   if (host == NULL) return NULL;
690 
691   if (*host == '\0') return NULL;
692 
693   ptr = start = text = strdup(host);
694 
695   if ((slash=strchr(text,'/')) != NULL) {
696     if (*(slash+1) == '\0') {
697       out_log(LEVEL_NORMAL,"ERROR netmask can't be empty (input text: %s)\n",host);
698       free(text); return NULL;
699     }
700     netmask = strtoul(slash+1,&ptr,10);
701     if (*ptr != '\0') {
702       out_log(LEVEL_NORMAL,"ERROR invalid netmask (input text: %s)\n",host);
703       free(text); return NULL;
704     }
705     *slash = '\0';
706     ptr = text;
707   }
708 
709   if (*ptr == '[') { /* try IPv6 reference */
710     while (*ptr && *ptr != ']') ptr++;
711     if (*ptr == '\0') return NULL; /* malformed IPv6 reference */
712     *ptr = '\0';
713     start = text+1;
714 
715     if (!string_is_ipv6(ptr)) {
716       out_log(LEVEL_NORMAL,"ERROR invalid IPv6 address (input text: %s)\n",host);
717       free(text); return NULL;
718     }
719 
720     type = HT_IPV6_REFERENCE;
721   } else { /* hostname, or IPv4 address */
722     if (string_is_ipv4(text)) {
723       type = HT_IPV4_ADDRESS;
724     }
725     else if (string_is_hostname(text)) {
726       type = HT_HOSTNAME;
727       if (netmask != 0) {
728         out_log(LEVEL_NORMAL,"ERROR netmask specified with a hostname ! (input text: %s)\n",host);
729         free(text); return NULL;
730       }
731     }
732     else {
733       out_log(LEVEL_NORMAL,"ERROR invalid address (input text: %s)\n",host);
734       free(text); return NULL;
735     }
736   }
737 
738   ip = ip_create();
739 
740   ip->type = type;
741   wzd_strncpy(ip->raw,start,sizeof(ip->raw));
742   ip->netmask = netmask;
743   free(text);
744 
745   return ip;
746 }
747 
748 
749 
750 
751 /** \brief Check if string is a numeric IPv4 address
752  * \return 1 if assertion is true
753  *
754  * \note actually, this is a very limited check
755  */
string_is_ipv4(const char * s)756 static int string_is_ipv4(const char * s)
757 {
758   while (*s != '\0') {
759     if (*s != '.' && !isdigit(*s)) return 0;
760     s++;
761   }
762 
763   return 1;
764 }
765 
766 /** \brief Check if string is a numeric IPv6 address
767  * \return 1 if assertion is true
768  *
769  * \note actually, this is a very limited check
770  */
string_is_ipv6(const char * s)771 static int string_is_ipv6(const char * s)
772 {
773   while (*s != '\0') {
774     if (*s != ':' && !isxdigit(*s)) return 0;
775     s++;
776   }
777 
778   return 1;
779 }
780 
781 /** \brief Check if string is a host name
782  * \return 1 if assertion is true
783  *
784  * Accepted host names are:
785  * [:alnum:] ([alnum] | . | -)*
786  */
string_is_hostname(const char * s)787 static int string_is_hostname(const char * s)
788 {
789   if (*s == '\0' || !isalnum(*s)) return 0;
790   s++;
791 
792   while (*s != '\0') {
793     if (!isalnum(*s) && *s != '-' && *s != '.') return 0;
794     s++;
795   }
796 
797   return 1;
798 }
799 
800