util: Avoid overflow in GetNameByINet
[openafs.git] / src / util / hostparse.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * ALL RIGHTS RESERVED
12  */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18
19 #ifdef AFS_NT40_ENV
20 #include <direct.h>
21 #else
22 #include <ctype.h>
23 #endif
24
25 #include "afsutil.h"
26
27 /* also parse a.b.c.d addresses */
28 struct hostent *
29 hostutil_GetHostByName(char *ahost)
30 {
31     int tc;
32     static struct hostent thostent;
33     static char *addrp[2];
34     static char addr[4];
35     char *ptr = ahost;
36     afs_uint32 tval, numeric = 0;
37     int dots = 0;
38
39     tc = *ahost;                /* look at the first char */
40     if (tc >= '0' && tc <= '9') {
41         numeric = 1;
42         while ((tc = *ptr++)) {
43             if (tc == '.') {
44                 if (dots >= 3) {
45                     numeric = 0;
46                     break;
47                 }
48                 dots++;
49             } else if (tc > '9' || tc < '0') {
50                 numeric = 0;
51                 break;
52             }
53         }
54     }
55     if (numeric) {
56         /* decimal address, return fake hostent with only hostaddr field good */
57         tval = 0;
58         dots = 0;
59         memset(addr, 0, sizeof(addr));
60         while ((tc = *ahost++)) {
61             if (tc == '.') {
62                 if (dots >= 3)
63                     return NULL;        /* too many dots */
64                 addr[dots++] = tval;
65                 tval = 0;
66             } else if (tc > '9' || tc < '0')
67                 return NULL;
68             else {
69                 tval *= 10;
70                 tval += tc - '0';
71             }
72         }
73         addr[dots] = tval;
74 #ifdef h_addr
75         /* 4.3 system */
76         addrp[0] = addr;
77         addrp[1] = NULL;
78         thostent.h_addr_list = &addrp[0];
79 #else /* h_addr */
80         /* 4.2 and older systems */
81         thostent.h_addr = addr;
82 #endif /* h_addr */
83         return &thostent;
84     } else {
85 #ifdef AFS_NT40_ENV
86         if (afs_winsockInit() < 0)
87             return NULL;
88 #endif
89         return gethostbyname(ahost);
90     }
91 }
92
93 /* Translate an internet address into a nice printable string. The
94  * variable addr is in network byte order.
95  */
96 char *
97 hostutil_GetNameByINet(afs_uint32 addr)
98 {
99     struct hostent *th;
100     static char tbuffer[256];
101
102 #ifdef AFS_NT40_ENV
103     if (afs_winsockInit() < 0)
104         return NULL;
105 #endif
106     th = gethostbyaddr((void *)&addr, sizeof(addr), AF_INET);
107     if (th && strlen(th->h_name) < sizeof(tbuffer)) {
108         strlcpy(tbuffer, th->h_name, sizeof(tbuffer));
109     } else {
110         addr = ntohl(addr);
111         sprintf(tbuffer, "%d.%d.%d.%d", (int)((addr >> 24) & 0xff),
112                 (int)((addr >> 16) & 0xff), (int)((addr >> 8) & 0xff),
113                 (int)(addr & 0xff));
114     }
115
116     return tbuffer;
117 }
118
119 /* the parameter is a pointer to a buffer containing a string of
120 ** bytes of the form
121 ** w.x.y.z      # machineName
122 ** returns the network interface in network byte order
123 */
124
125 #define MAXBYTELEN 32
126 afs_uint32
127 extractAddr(char *line, int maxSize)
128 {
129     char byte1[MAXBYTELEN], byte2[MAXBYTELEN];
130     char byte3[MAXBYTELEN], byte4[MAXBYTELEN];
131     int i = 0;
132     char *endPtr;
133     afs_uint32 val1, val2, val3, val4;
134     afs_uint32 val = 0;
135
136     /* skip empty spaces */
137     while (isspace(*line) && maxSize) {
138         line++;
139         maxSize--;
140     }
141
142     /* skip empty lines */
143     if (!maxSize || !*line)
144         return AFS_IPINVALIDIGNORE;
145
146     while ((*line != '.') && maxSize) { /* extract first byte */
147         if (!isdigit(*line))
148             return AFS_IPINVALID;
149         if (i >= MAXBYTELEN-1)
150             return AFS_IPINVALID;       /* no space */
151         byte1[i++] = *line++;
152         maxSize--;
153     }
154     if (!maxSize)
155         return AFS_IPINVALID;
156     byte1[i] = 0;
157
158     i = 0, line++;
159     while ((*line != '.') && maxSize) { /* extract second byte */
160         if (!isdigit(*line))
161             return AFS_IPINVALID;
162         if (i >= MAXBYTELEN-1)
163             return AFS_IPINVALID;       /* no space */
164         byte2[i++] = *line++;
165         maxSize--;
166     }
167     if (!maxSize)
168         return AFS_IPINVALID;
169     byte2[i] = 0;
170
171     i = 0, line++;
172     while ((*line != '.') && maxSize) {
173         if (!isdigit(*line))
174             return AFS_IPINVALID;
175         if (i >= MAXBYTELEN-1)
176             return AFS_IPINVALID;       /* no space */
177         byte3[i++] = *line++;
178         maxSize--;
179     }
180     if (!maxSize)
181         return AFS_IPINVALID;
182     byte3[i] = 0;
183
184     i = 0, line++;
185     while (*line && !isspace(*line) && maxSize) {
186         if (!isdigit(*line))
187             return AFS_IPINVALID;
188         if (i >= MAXBYTELEN-1)
189             return AFS_IPINVALID;       /* no space */
190         byte4[i++] = *line++;
191         maxSize--;
192     }
193     if (!maxSize)
194         return AFS_IPINVALID;
195     byte4[i] = 0;
196
197     errno = 0;
198     val1 = strtol(byte1, &endPtr, 10);
199     if ((val1 == 0) && (errno != 0 || byte1 == endPtr))
200         return AFS_IPINVALID;
201
202     errno = 0;
203     val2 = strtol(byte2, &endPtr, 10);
204     if ((val2 == 0) && (errno != 0 || byte2 == endPtr)) /* no conversion */
205         return AFS_IPINVALID;
206
207     errno = 0;
208     val3 = strtol(byte3, &endPtr, 10);
209     if ((val3 == 0) && (errno != 0 || byte3 == endPtr)) /* no conversion */
210         return AFS_IPINVALID;
211
212     errno = 0;
213     val4 = strtol(byte4, &endPtr, 10);
214     if ((val4 == 0) && (errno != 0 || byte4 == endPtr)) /* no conversion */
215         return AFS_IPINVALID;
216
217     val = (val1 << 24) | (val2 << 16) | (val3 << 8) | val4;
218     val = htonl(val);
219     return val;
220 }
221
222 /* same as inet_ntoa, but to a non-static buffer, must be freed by called */
223 char *
224 afs_inet_ntoa_r(afs_uint32 addr, char *buf)
225 {
226     int temp;
227
228     temp = ntohl(addr);
229     sprintf(buf, "%d.%d.%d.%d", (temp >> 24) & 0xff, (temp >> 16) & 0xff,
230             (temp >> 8) & 0xff, (temp) & 0xff);
231     return buf;
232 }
233
234 /*
235  * gettmpdir() -- Returns pointer to global temporary directory string.
236  *     Always succeeds.  Never attempt to deallocate directory string.
237  */
238
239 char *
240 gettmpdir(void)
241 {
242     char *tmpdirp = NULL;
243
244 #ifdef AFS_NT40_ENV
245     static char *saveTmpDir = NULL;
246
247     if (saveTmpDir == NULL) {
248         /* initialize global temporary directory string */
249         char *dirp = malloc(MAX_PATH+1);
250         int freeDirp = 1;
251
252         if (dirp != NULL) {
253             DWORD pathLen = GetTempPath(MAX_PATH+1, dirp);
254
255             if (pathLen == 0 || pathLen > MAX_PATH) {
256                 /* can't get tmp path; get cur work dir */
257                 pathLen = GetCurrentDirectory(MAX_PATH, dirp);
258                 if (pathLen == 0 || pathLen > MAX_PATH) {
259                     free(dirp);
260                     dirp = NULL;
261                 }
262             }
263
264             if (dirp != NULL) {
265                 /* Have a valid dir path; check that actually exists. */
266                 DWORD fileAttr = GetFileAttributes(dirp);
267
268                 if ((fileAttr == 0xFFFFFFFF)
269                     || ((fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
270                     free(dirp);
271                     dirp = NULL;
272                 }
273             }
274         }
275
276         if (dirp != NULL) {
277             FilepathNormalize(dirp);
278         } else {
279             /* most likely TMP or TEMP env vars specify a non-existent dir */
280             dirp = "/";
281             freeDirp = 0;
282         }
283
284         /* atomically initialize shared buffer pointer IF still null */
285         if (InterlockedCompareExchangePointer(&saveTmpDir, dirp, NULL) != NULL) {
286             /* shared buffer pointer already initialized by another thread */
287             if (freeDirp)
288                 free(dirp);
289         }
290     }
291     /* if (!saveTmpDir) */
292     tmpdirp = saveTmpDir;
293 #else
294     tmpdirp = "/tmp";
295 #endif /* AFS_NT40_ENV */
296
297     return tmpdirp;
298 }