Bug Summary

File:src/usr.bin/whois/whois.c
Warning:line 233, column 4
Potential leak of memory pointed to by 'p'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name whois.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/whois/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/whois/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/whois/whois.c
1/* $OpenBSD: whois.c,v 1.58 2018/06/19 11:28:11 jca Exp $ */
2
3/*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <sys/socket.h>
34
35#include <netinet/in.h>
36#include <arpa/inet.h>
37#include <netdb.h>
38
39#include <ctype.h>
40#include <err.h>
41#include <errno(*__errno()).h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#define NICHOST"whois.crsnic.net" "whois.crsnic.net"
48#define INICHOST"whois.networksolutions.com" "whois.networksolutions.com"
49#define CNICHOST"whois.corenic.net" "whois.corenic.net"
50#define DNICHOST"whois.nic.mil" "whois.nic.mil"
51#define GNICHOST"whois.nic.gov" "whois.nic.gov"
52#define ANICHOST"whois.arin.net" "whois.arin.net"
53#define RNICHOST"whois.ripe.net" "whois.ripe.net"
54#define PNICHOST"whois.apnic.net" "whois.apnic.net"
55#define RUNICHOST"whois.ripn.net" "whois.ripn.net"
56#define MNICHOST"whois.ra.net" "whois.ra.net"
57#define LNICHOST"whois.lacnic.net" "whois.lacnic.net"
58#define AFNICHOST"whois.afrinic.net" "whois.afrinic.net"
59#define BNICHOST"whois.registro.br" "whois.registro.br"
60#define PDBHOST"whois.peeringdb.com" "whois.peeringdb.com"
61#define IANAHOST"whois.iana.org" "whois.iana.org"
62#define QNICHOST_TAIL".whois-servers.net" ".whois-servers.net"
63
64#define WHOIS_PORT"whois" "whois"
65#define WHOIS_SERVER_ID"Registrar WHOIS Server:" "Registrar WHOIS Server:"
66
67#define WHOIS_RECURSE0x01 0x01
68#define WHOIS_QUICK0x02 0x02
69
70const char *port_whois = WHOIS_PORT"whois";
71const char *ip_whois[] = { LNICHOST"whois.lacnic.net", RNICHOST"whois.ripe.net", PNICHOST"whois.apnic.net", BNICHOST"whois.registro.br",
72 AFNICHOST"whois.afrinic.net", NULL((void *)0) };
73
74__dead__attribute__((__noreturn__)) void usage(void);
75int whois(const char *, const char *, const char *, int);
76char *choose_server(const char *, const char *, char **);
77
78int
79main(int argc, char *argv[])
80{
81 int ch, flags, rval;
82 char *host, *name, *country;
83
84 country = host = NULL((void *)0);
85 flags = rval = 0;
86 while ((ch = getopt(argc, argv, "aAc:dgh:iIlmp:PqQrR")) != -1)
1
Assuming the condition is false
2
Loop condition is false. Execution continues on line 139
87 switch (ch) {
88 case 'a':
89 host = ANICHOST"whois.arin.net";
90 break;
91 case 'A':
92 host = PNICHOST"whois.apnic.net";
93 break;
94 case 'c':
95 country = optarg;
96 break;
97 case 'd':
98 host = DNICHOST"whois.nic.mil";
99 break;
100 case 'g':
101 host = GNICHOST"whois.nic.gov";
102 break;
103 case 'h':
104 host = optarg;
105 break;
106 case 'i':
107 host = INICHOST"whois.networksolutions.com";
108 break;
109 case 'I':
110 host = IANAHOST"whois.iana.org";
111 break;
112 case 'l':
113 host = LNICHOST"whois.lacnic.net";
114 break;
115 case 'm':
116 host = MNICHOST"whois.ra.net";
117 break;
118 case 'p':
119 port_whois = optarg;
120 break;
121 case 'P':
122 host = PDBHOST"whois.peeringdb.com";
123 break;
124 case 'q':
125 /* deprecated, now the default */
126 break;
127 case 'Q':
128 flags |= WHOIS_QUICK0x02;
129 break;
130 case 'r':
131 host = RNICHOST"whois.ripe.net";
132 break;
133 case 'R':
134 host = RUNICHOST"whois.ripn.net";
135 break;
136 default:
137 usage();
138 }
139 argc -= optind;
140 argv += optind;
141
142 if (!argc || (country
3.1
'country' is equal to NULL
!= NULL((void *)0) && host != NULL((void *)0)))
3
Assuming 'argc' is not equal to 0
143 usage();
144
145 if (pledge("stdio dns inet", NULL((void *)0)) == -1)
4
Assuming the condition is false
5
Taking false branch
146 err(1, "pledge");
147
148 if (host
5.1
'host' is equal to NULL
== NULL((void *)0) && country
5.2
'country' is equal to NULL
== NULL((void *)0) && !(flags & WHOIS_QUICK0x02))
6
Taking true branch
149 flags |= WHOIS_RECURSE0x01;
150 for (name = *argv; (name = *argv) != NULL((void *)0); argv++) {
7
Assuming the condition is true
8
Loop condition is true. Entering loop body
151 char *tofree = NULL((void *)0);
152 const char *server =
153 host
8.1
'host' is null
? host : choose_server(name, country, &tofree);
9
'?' condition is false
154 rval += whois(name, server, port_whois, flags);
10
Calling 'whois'
155 free(tofree);
156 }
157 return (rval);
158}
159
160int
161whois(const char *query, const char *server, const char *port, int flags)
162{
163 FILE *fp;
164 char *buf, *p, *nhost, *nbuf = NULL((void *)0);
165 size_t len;
166 int i, s, error;
167 const char *reason = NULL((void *)0), *fmt;
168 struct addrinfo hints, *res, *ai;
169
170 memset(&hints, 0, sizeof(hints));
171 hints.ai_flags = 0;
172 hints.ai_family = AF_UNSPEC0;
173 hints.ai_socktype = SOCK_STREAM1;
174 error = getaddrinfo(server, port, &hints, &res);
175 if (error) {
11
Assuming 'error' is 0
12
Taking false branch
176 if (error == EAI_SERVICE-8)
177 warnx("%s: bad port", port);
178 else
179 warnx("%s: %s", server, gai_strerror(error));
180 return (1);
181 }
182
183 for (s = -1, ai = res; ai != NULL((void *)0); ai = ai->ai_next) {
13
Assuming 'ai' is not equal to NULL
14
Loop condition is true. Entering loop body
184 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
185 if (s == -1) {
15
Assuming the condition is false
16
Taking false branch
186 error = errno(*__errno());
187 reason = "socket";
188 continue;
189 }
190 if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1) {
17
Assuming the condition is false
18
Taking false branch
191 error = errno(*__errno());
192 reason = "connect";
193 close(s);
194 s = -1;
195 continue;
196 }
197 break; /*okay*/
19
Execution continues on line 199
198 }
199 freeaddrinfo(res);
200 if (s == -1) {
20
Taking false branch
201 if (reason) {
202 errno(*__errno()) = error;
203 warn("%s: %s", server, reason);
204 } else
205 warn("unknown error in connection attempt");
206 return (1);
207 }
208
209 if (strcmp(server, "whois.denic.de") == 0 ||
21
Taking false branch
210 strcmp(server, "de" QNICHOST_TAIL".whois-servers.net") == 0)
211 fmt = "-T dn,ace -C ISO-8859-1 %s\r\n";
212 else if (strcmp(server, "whois.dk-hostmaster.dk") == 0 ||
22
Taking false branch
213 strcmp(server, "dk" QNICHOST_TAIL".whois-servers.net") == 0)
214 fmt = "--show-handles %s\r\n";
215 else
216 fmt = "%s\r\n";
217
218 fp = fdopen(s, "r+");
219 if (fp == NULL((void *)0))
23
Assuming 'fp' is not equal to NULL
24
Taking false branch
220 err(1, "fdopen");
221 fprintf(fp, fmt, query);
222 fflush(fp);
223 nhost = NULL((void *)0);
224 while ((buf = fgetln(fp, &len)) != NULL((void *)0)) {
25
Assuming the condition is true
26
Loop condition is true. Entering loop body
38
Assuming the condition is true
39
Loop condition is true. Entering loop body
225 p = buf + len - 1;
226 if (isspace((unsigned char)*p)) {
27
Taking false branch
40
Taking false branch
227 do
228 *p = '\0';
229 while (p > buf && isspace((unsigned char)*--p));
230 } else {
231 if ((nbuf = malloc(len + 1)) == NULL((void *)0))
28
Memory is allocated
29
Assuming the condition is false
30
Taking false branch
41
Assuming the condition is false
42
Taking false branch
232 err(1, "malloc");
233 memcpy(nbuf, buf, len);
43
Potential leak of memory pointed to by 'p'
234 nbuf[len] = '\0';
235 buf = nbuf;
236 }
237 puts(buf);
238
239 if (nhost
30.1
'nhost' is equal to NULL
!= NULL((void *)0) || !(flags & WHOIS_RECURSE0x01))
31
Taking false branch
240 continue;
241
242 if ((p = strstr(buf, WHOIS_SERVER_ID"Registrar WHOIS Server:"))) {
32
Assuming 'p' is null
33
Taking false branch
243 p += sizeof(WHOIS_SERVER_ID"Registrar WHOIS Server:") - 1;
244 while (isblank((unsigned char)*p))
245 p++;
246 if ((len = strcspn(p, " \t\n\r"))) {
247 if ((nhost = malloc(len + 1)) == NULL((void *)0))
248 err(1, "malloc");
249 memcpy(nhost, p, len);
250 nhost[len] = '\0';
251 }
252 } else if (strcmp(server, ANICHOST"whois.arin.net") == 0) {
34
Taking true branch
253 for (p = buf; *p != '\0'; p++)
35
Loop condition is false. Execution continues on line 255
254 *p = tolower((unsigned char)*p);
255 for (i = 0; ip_whois[i] != NULL((void *)0); i++) {
36
Assuming the condition is false
37
Loop condition is false. Execution continues on line 224
256 if (strstr(buf, ip_whois[i]) != NULL((void *)0)) {
257 nhost = strdup(ip_whois[i]);
258 if (nhost == NULL((void *)0))
259 err(1, "strdup");
260 break;
261 }
262 }
263 }
264 }
265 fclose(fp);
266 free(nbuf);
267
268 if (nhost != NULL((void *)0)) {
269 error = whois(query, nhost, port, 0);
270 free(nhost);
271 }
272
273 return (error);
274}
275
276/*
277 * If no country is specified determine the top level domain from the query.
278 * If the TLD is a number, query ARIN, otherwise, use TLD.whois-server.net.
279 * If the domain does not contain '.', check to see if it is an NSI handle
280 * (starts with '!') or a CORE handle (COCO-[0-9]+ or COHO-[0-9]+) or an
281 * ASN (starts with AS) or IPv6 address (contains ':'). Fall back to
282 * NICHOST for the non-handle and non-IPv6 case.
283 */
284char *
285choose_server(const char *name, const char *country, char **tofree)
286{
287 char *server;
288 const char *qhead;
289 char *ep;
290 struct addrinfo hints, *res = NULL((void *)0);
291
292 memset(&hints, 0, sizeof(hints));
293 hints.ai_flags = 0;
294 hints.ai_family = AF_UNSPEC0;
295 hints.ai_socktype = SOCK_STREAM1;
296
297 if (country != NULL((void *)0))
298 qhead = country;
299 else if ((qhead = strrchr(name, '.')) == NULL((void *)0)) {
300 if (*name == '!')
301 return (INICHOST"whois.networksolutions.com");
302 else if ((strncasecmp(name, "COCO-", 5) == 0 ||
303 strncasecmp(name, "COHO-", 5) == 0) &&
304 strtol(name + 5, &ep, 10) > 0 && *ep == '\0')
305 return (CNICHOST"whois.corenic.net");
306 else if ((strncasecmp(name, "AS", 2) == 0) &&
307 strtol(name + 2, &ep, 10) > 0 && *ep == '\0')
308 return (MNICHOST"whois.ra.net");
309 else if (strchr(name, ':') != NULL((void *)0)) /* IPv6 address */
310 return (ANICHOST"whois.arin.net");
311 else
312 return (NICHOST"whois.crsnic.net");
313 } else if (isdigit((unsigned char)*(++qhead)))
314 return (ANICHOST"whois.arin.net");
315
316 /*
317 * Post-2003 ("new") gTLDs are all supposed to have "whois.nic.domain"
318 * (per registry agreement), some older gTLDs also support this...
319 */
320 if (asprintf(&server, "whois.nic.%s", qhead) == -1)
321 err(1, NULL((void *)0));
322
323 /* most ccTLDs don't do this, but QNICHOST/whois-servers mostly works */
324 if ((strlen(qhead) == 2 ||
325 /* and is required for most of the <=2003 TLDs/gTLDs */
326 strcasecmp(qhead, "org") == 0 ||
327 strcasecmp(qhead, "com") == 0 ||
328 strcasecmp(qhead, "net") == 0 ||
329 strcasecmp(qhead, "cat") == 0 ||
330 strcasecmp(qhead, "pro") == 0 ||
331 strcasecmp(qhead, "info") == 0 ||
332 strcasecmp(qhead, "aero") == 0 ||
333 strcasecmp(qhead, "jobs") == 0 ||
334 strcasecmp(qhead, "mobi") == 0 ||
335 strcasecmp(qhead, "museum") == 0 ||
336 /* for others, if whois.nic.TLD doesn't exist, try whois-servers */
337 getaddrinfo(server, NULL((void *)0), &hints, &res) != 0)) {
338 free(server);
339 if (asprintf(&server, "%s%s", qhead, QNICHOST_TAIL".whois-servers.net") == -1)
340 err(1, NULL((void *)0));
341 }
342 if (res != NULL((void *)0))
343 freeaddrinfo(res);
344
345 *tofree = server;
346 return (server);
347}
348
349__dead__attribute__((__noreturn__)) void
350usage(void)
351{
352 extern char *__progname;
353
354 fprintf(stderr(&__sF[2]),
355 "usage: %s [-AadgIilmPQRr] [-c country-code | -h host] "
356 "[-p port] name ...\n", __progname);
357 exit(1);
358}