1/* Copyright (c) 2020-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation; version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
16
17/* cachedumper - dump a human-readable representation of a cache file. */
18
19#include <ctype.h>
20#include <stdio.h>
21#include <fcntl.h>
22#include <unistd.h>
23#include <libintl.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/mman.h>
27#include <arpa/inet.h>
28#include <getopt.h>
29#include <sys/param.h>
30
31#include "nscd.h"
32#include "dbg_log.h"
33
34static void *the_cache;
35
36#define NO_REF ((ref_t) -1)
37
38/* Given a chunk of raw data CP of length LEN, print it in a hopefully
39 user-readable format, including colorizing non-readable characters.
40 STR prefixes it, if non-NULL. If LEN is -1, CP is
41 NUL-terminated. */
42unsigned char *
43data_string (unsigned char *cp, const char *str, int len)
44{
45 int oops = 0;
46 unsigned char *cpe = cp + len;
47 printf ("%s", str);
48 while (len == -1 || cp < cpe)
49 {
50 if (isgraph (*cp))
51 putchar (c: *cp);
52 else
53 printf ("\033[%dm<%02x>\033[0m", *cp % 6 + 31, *cp);
54 if (len == -1 && *cp == 0)
55 return cp + 1;
56
57 ++cp;
58 if (++oops > 1000)
59 break;
60 }
61 return cp;
62}
63
64void
65nscd_print_cache (const char *name)
66{
67 struct stat st;
68 int fd;
69 int i;
70
71 if (stat (file: name, buf: &st) < 0)
72 {
73 perror (s: name);
74 exit (status: 1);
75 }
76
77 fd = open (file: name, O_RDONLY);
78
79 the_cache = mmap (NULL, len: st.st_size, PROT_READ, MAP_PRIVATE, fd: fd, offset: 0);
80
81 struct database_pers_head *dps = (struct database_pers_head *) the_cache;
82
83 /* Shortcut for "print the cache offset (address) of X in the
84 cache". */
85#define A(x) (int) ((char *) &(x) - (char *) the_cache)
86
87 /* Common code for "print field DPS->F, it's offset, and contents". */
88#define DPS(f) printf("%08x: %24s : %10d %08x\n", A (dps->f), #f, (int) dps->f, (int) dps->f);
89
90 if (debug_level > 0)
91 {
92 DPS (version);
93 DPS (header_size);
94 DPS (gc_cycle);
95 DPS (nscd_certainly_running);
96 DPS (timestamp);
97 DPS (module);
98 DPS (data_size);
99 DPS (first_free);
100 DPS (nentries);
101 DPS (maxnentries);
102 DPS (maxnsearched);
103 DPS (poshit);
104 DPS (neghit);
105 DPS (posmiss);
106 DPS (negmiss);
107 DPS (rdlockdelayed);
108 DPS (wrlockdelayed);
109 DPS (addfailed);
110 printf ("\n");
111 }
112
113
114 char *data = (char *) &dps->array[roundup (dps->module,
115 ALIGN / sizeof (ref_t))];
116
117 /* Loop through each entry in the hash table, which is of size
118 dps->module. Raw data is stored after the hash table in the
119 cache file. */
120 for (i = 0; i < dps->module; i++)
121 {
122 ref_t r = dps->array[i];
123 if (r == NO_REF)
124 continue;
125
126 if (debug_level > 2)
127 printf ("hash[%4d] = 0x%x\n", i, r);
128
129 while (r != NO_REF)
130 {
131 struct hashentry *here = (struct hashentry *) (data + r);
132
133 unsigned char *key = (unsigned char *) data + here->key;
134
135 printf ("\n%08x: type %s key %p \"", A (*here),
136 serv2str[here->type], key);
137
138 data_string (cp: key, str: "", len: here->len);
139
140 struct datahead *dh = (struct datahead *) (data + here->packet);
141 printf ("\" (len:%ld) Data %08lx\n", (long) here->len,
142 (long unsigned int) ((char *) dh - (char *) the_cache));
143
144 if (debug_level > 0)
145 {
146/* Common code for printing fields in struct DATAHEAD DH. */
147#define DH(f) printf ("%08x; %24s : %10d %08x\n", A (dh->f), #f, (int) dh->f, (int) dh->f);
148 DH (allocsize);
149 DH (recsize);
150 DH (timeout);
151 DH (notfound);
152 DH (nreloads);
153 DH (usable);
154 DH (unused);
155 DH (ttl);
156 }
157
158 unsigned char *cp = (unsigned char *) (&dh->data[0]);
159 unsigned char *cpe =
160 (unsigned char *) (&dh->data[0]) + dh->allocsize;
161
162
163 int i;
164 uint32_t *grplens;
165
166 if (debug_level > 1)
167 {
168 data_string (cp, _(" - all data: "), len: cpe - cp);
169 printf ("\n");
170 }
171
172 /* These two are common to all responses. */
173 printf ("V%d F%d",
174 dh->data[0].pwdata.version, dh->data[0].pwdata.found);
175
176/* Shortcut for the common case where we iterate through
177 fixed-length strings stored in the data portion of the
178 cache. CP is updated to point to the next string. */
179#define DSTR(str, l) cp = data_string (cp, str, l)
180
181 switch (here->type)
182 {
183 case GETPWBYNAME:
184 case GETPWBYUID:
185 {
186 pw_response_header *pw = &(dh->data[0].pwdata);
187 cp += sizeof (*pw);
188 DSTR (" name ", pw->pw_name_len);
189 DSTR (" passwd ", pw->pw_passwd_len);
190 printf (" uid %d gid %d", pw->pw_uid, pw->pw_gid);
191 DSTR (" gecos ", pw->pw_gecos_len);
192 DSTR (" dir ", pw->pw_dir_len);
193 DSTR (" shell ", pw->pw_shell_len);
194 DSTR (" byuid ", -1);
195 DSTR (" key ", -1);
196 printf ("\n");
197 }
198 break;
199
200 case GETGRBYNAME:
201 case GETGRBYGID:
202 {
203 gr_response_header *gr = &(dh->data[0].grdata);
204 cp += sizeof (*gr);
205 grplens = (uint32_t *) cp;
206 cp += gr->gr_mem_cnt * sizeof (uint32_t);
207 DSTR (" name ", gr->gr_name_len);
208 DSTR (" passwd ", gr->gr_passwd_len);
209 printf (" gid %d members %d [ ", (int) gr->gr_gid,
210 (int) gr->gr_mem_cnt);
211 for (i = 0; i < gr->gr_mem_cnt; i++)
212 DSTR (" ", grplens[i]);
213 DSTR (" ] bygid ", -1);
214 DSTR (" key ", -1);
215 printf ("\n");
216 }
217 break;
218
219 case GETHOSTBYADDR:
220 case GETHOSTBYADDRv6:
221 case GETHOSTBYNAME:
222 case GETHOSTBYNAMEv6:
223 {
224 hst_response_header *hst = &(dh->data[0].hstdata);
225 printf (" addrtype %d error %d", hst->h_addrtype, hst->error);
226 cp += sizeof (*hst);
227 DSTR (" name ", hst->h_name_len);
228 uint32_t *aliases_len = (uint32_t *) cp;
229 cp += hst->h_aliases_cnt * sizeof (uint32_t);
230 uint32_t *addrs = (uint32_t *) cp;
231 cp += hst->h_length * hst->h_addr_list_cnt;
232
233 if (hst->h_aliases_cnt)
234 {
235 printf (" aliases [");
236 for (i = 0; i < hst->h_aliases_cnt; i++)
237 DSTR (" ", aliases_len[i]);
238 printf (" ]");
239 }
240 if (hst->h_addr_list_cnt)
241 {
242 char buf[INET6_ADDRSTRLEN];
243 printf (" addresses [");
244 for (i = 0; i < hst->h_addr_list_cnt; i++)
245 {
246 inet_ntop (af: hst->h_addrtype, cp: addrs, buf: buf, len: sizeof (buf));
247 printf (" %s", buf);
248 addrs += hst->h_length;
249 }
250 printf (" ]");
251 }
252
253 printf ("\n");
254 }
255 break;
256
257 case GETAI:
258 {
259 ai_response_header *ai = &(dh->data[0].aidata);
260 printf (" naddrs %ld addrslen %ld canonlen %ld error %d [",
261 (long) ai->naddrs, (long) ai->addrslen,
262 (long) ai->canonlen, ai->error);
263 cp += sizeof (*ai);
264 unsigned char *addrs = cp;
265 unsigned char *families = cp + ai->addrslen;
266 cp = families + ai->naddrs;
267 char buf[INET6_ADDRSTRLEN];
268
269 for (i = 0; i < ai->naddrs; i++)
270 {
271 switch (*families)
272 {
273 case AF_INET:
274 inet_ntop (af: *families, cp: addrs, buf: buf, len: sizeof (buf));
275 printf (" %s", buf);
276 addrs += 4;
277 break;
278 case AF_INET6:
279 inet_ntop (af: *families, cp: addrs, buf: buf, len: sizeof (buf));
280 printf (" %s", buf);
281 addrs += 16;
282 break;
283 }
284 families++;
285 }
286 DSTR (" ] canon ", ai->canonlen);
287 DSTR (" key ", -1);
288 printf ("\n");
289 }
290 break;
291
292 case INITGROUPS:
293 {
294 initgr_response_header *ig = &(dh->data[0].initgrdata);
295 printf (" nresults %d groups [", (int) ig->ngrps);
296 cp += sizeof (*ig);
297 grplens = (uint32_t *) cp;
298 cp += ig->ngrps * sizeof (uint32_t);
299 for (i = 0; i < ig->ngrps; i++)
300 printf (" %d", grplens[i]);
301 DSTR (" ] key ", -1);
302 printf ("\n");
303 }
304 break;
305
306 case GETSERVBYNAME:
307 case GETSERVBYPORT:
308 {
309 serv_response_header *serv = &(dh->data[0].servdata);
310 printf (" alias_cnt %ld port %d (stored as %d)",
311 (long) serv->s_aliases_cnt,
312 ((serv->s_port & 0xff00) >> 8) | ((serv->
313 s_port & 0xff) <<
314 8), serv->s_port);
315 cp += sizeof (*serv);
316 DSTR (" name ", serv->s_name_len);
317 DSTR (" proto ", serv->s_proto_len);
318 if (serv->s_aliases_cnt)
319 {
320 uint32_t *alias_len = (uint32_t *) cp;
321 printf (" aliases [");
322 cp += sizeof (uint32_t) * serv->s_aliases_cnt;
323 for (i = 0; i < serv->s_aliases_cnt; i++)
324 DSTR (" ", alias_len[i]);
325 printf (" ]");
326 }
327 printf ("\n");
328 }
329 break;
330
331 case GETNETGRENT:
332 {
333 netgroup_response_header *ng = &(dh->data[0].netgroupdata);
334 printf (" nresults %d len %d\n",
335 (int) ng->nresults, (int) ng->result_len);
336 cp += sizeof (*ng);
337 for (i = 0; i < ng->nresults; i++)
338 {
339 DSTR (" (", -1);
340 DSTR (",", -1);
341 DSTR (",", -1);
342 printf (")");
343 }
344 printf ("\n");
345 }
346 break;
347
348 case INNETGR:
349 {
350 innetgroup_response_header *ing =
351 &(dh->data[0].innetgroupdata);
352 printf (" result %d\n", ing->result);
353 }
354 break;
355
356 default:
357 break;
358 }
359
360 if (debug_level > 2 && cp && cp < cpe)
361 {
362 printf (_(" - remaining data %p: "), cp);
363 data_string (cp, str: "", len: cpe - cp);
364 printf ("\n");
365 }
366
367
368 r = here->next;
369 }
370 }
371
372 munmap (addr: the_cache, len: st.st_size);
373
374 exit (status: 0);
375}
376

source code of glibc/nscd/cachedumper.c