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