Fix logic in gethostbyname_r (bug #1634)
[asterisk/asterisk.git] / utils.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Utility functions
5  *
6  * Copyright (C) 2004, Digium
7  *
8  * This program is free software, distributed under the terms of
9  * the GNU General Public License
10  */
11
12 #include <ctype.h>
13 #include <asterisk/lock.h>
14 #include <asterisk/utils.h>
15
16 #if defined(__FreeBSD__)
17
18 /* duh? ERANGE value copied from web... */
19 #define ERANGE 34
20 #undef gethostbyname
21
22 int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
23                         size_t buflen, struct hostent **result, 
24                         int *h_errnop) 
25 {
26         int hsave;
27         struct hostent *ph;
28         static ast_mutex_t __mutex = AST_MUTEX_INITIALIZER;
29         ast_mutex_lock(&__mutex); /* begin critical area */
30         hsave = h_errno;
31
32         ph = gethostbyname(name);
33         *h_errnop = h_errno; /* copy h_errno to *h_herrnop */
34         if (ph == NULL) {
35                 *result = NULL;
36         } else {
37                 char **p, **q;
38                 char *pbuf;
39                 int nbytes=0;
40                 int naddr=0, naliases=0;
41                 /* determine if we have enough space in buf */
42
43                 /* count how many addresses */
44                 for (p = ph->h_addr_list; *p != 0; p++) {
45                         nbytes += ph->h_length; /* addresses */
46                         nbytes += sizeof(*p); /* pointers */
47                         naddr++;
48                 }
49                 nbytes += sizeof(*p); /* one more for the terminating NULL */
50
51                 /* count how many aliases, and total length of strings */
52                 for (p = ph->h_aliases; *p != 0; p++) {
53                         nbytes += (strlen(*p)+1); /* aliases */
54                         nbytes += sizeof(*p);  /* pointers */
55                         naliases++;
56                 }
57                 nbytes += sizeof(*p); /* one more for the terminating NULL */
58
59                 /* here nbytes is the number of bytes required in buffer */
60                 /* as a terminator must be there, the minimum value is ph->h_length */
61                 if(nbytes > buflen) {
62                         *result = NULL;
63                         ast_mutex_unlock(&__mutex); /* end critical area */
64                         return ERANGE; /* not enough space in buf!! */
65                 }
66
67                 /* There is enough space. Now we need to do a deep copy! */
68                 /* Allocation in buffer:
69                         from [0] to [(naddr-1) * sizeof(*p)]:
70                         pointers to addresses
71                         at [naddr * sizeof(*p)]:
72                         NULL
73                         from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
74                         pointers to aliases
75                         at [(naddr+naliases+1) * sizeof(*p)]:
76                         NULL
77                         then naddr addresses (fixed length), and naliases aliases (asciiz).
78                 */
79
80                 *ret = *ph;   /* copy whole structure (not its address!) */
81
82                 /* copy addresses */
83                 q = (char **)buf; /* pointer to pointers area (type: char **) */
84                 ret->h_addr_list = q; /* update pointer to address list */
85                 pbuf = buf + ((naddr+naliases+2)*sizeof(*p)); /* skip that area */
86                 for (p = ph->h_addr_list; *p != 0; p++) {
87                         memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
88                         *q++ = pbuf; /* the pointer is the one inside buf... */
89                         pbuf += ph->h_length; /* advance pbuf */
90                 }
91                 *q++ = NULL; /* address list terminator */
92
93                 /* copy aliases */
94                 ret->h_aliases = q; /* update pointer to aliases list */
95                 for (p = ph->h_aliases; *p != 0; p++) {
96                         strcpy(pbuf, *p); /* copy alias strings */
97                         *q++ = pbuf; /* the pointer is the one inside buf... */
98                         pbuf += strlen(*p); /* advance pbuf */
99                         *pbuf++ = 0; /* string terminator */
100                 }
101                 *q++ = NULL; /* terminator */
102
103                 strcpy(pbuf, ph->h_name); /* copy alias strings */
104                 ret->h_name = pbuf;
105                 pbuf += strlen(ph->h_name); /* advance pbuf */
106                 *pbuf++ = 0; /* string terminator */
107
108                 *result = ret;  /* and let *result point to structure */
109
110         }
111         h_errno = hsave;  /* restore h_errno */
112         ast_mutex_unlock(&__mutex); /* end critical area */
113
114         return (*result == NULL); /* return 0 on success, non-zero on error */
115 }
116
117
118 #endif
119
120 struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
121 {
122         int res;
123         int herrno;
124         const char *s;
125         struct hostent *result = NULL;
126         /* Although it is perfectly legitimate to lookup a pure integer, for
127            the sake of the sanity of people who like to name their peers as
128            integers, we break with tradition and refuse to look up a
129            pure integer */
130         s = host;
131         while(s && *s) {
132                 if (!isdigit(*s))
133                         break;
134                 s++;
135         }
136         if (!s || !*s)
137                 return NULL;
138         res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
139
140         if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
141                 return NULL;
142         return &hp->hp;
143 }