| 1 | |
| 2 | * Copyright (c) 2004-2006 The NetBSD Foundation, Inc.
|
| 3 | * All rights reserved.
|
| 4 | *
|
| 5 | * This code is derived from software contributed to The NetBSD Foundation
|
| 6 | * by Luke Mewburn.
|
| 7 | * Timo Teräs cleaned up the code for use in Alpine Linux with musl libc.
|
| 8 | *
|
| 9 | * Redistribution and use in source and binary forms, with or without
|
| 10 | * modification, are permitted provided that the following conditions
|
| 11 | * are met:
|
| 12 | * 1. Redistributions of source code must retain the above copyright
|
| 13 | * notice, this list of conditions and the following disclaimer.
|
| 14 | * 2. Redistributions in binary form must reproduce the above copyright
|
| 15 | * notice, this list of conditions and the following disclaimer in the
|
| 16 | * documentation and/or other materials provided with the distribution.
|
| 17 | *
|
| 18 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
| 19 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
| 20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
| 21 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
| 22 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
| 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
| 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
| 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
| 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
| 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 28 | * POSSIBILITY OF SUCH DAMAGE.
|
| 29 |
|
| 30 |
|
| 31 | #include <sys/socket.h>
|
| 32 | #include <sys/param.h>
|
| 33 | #include <ctype.h>
|
| 34 | #include <errno.h>
|
| 35 | #include <limits.h>
|
| 36 | #include <netdb.h>
|
| 37 | #include <pwd.h>
|
| 38 | #include <grp.h>
|
| 39 | #include <shadow.h>
|
| 40 | #include <stdio.h>
|
| 41 | #include <stdarg.h>
|
| 42 | #include <stdbool.h>
|
| 43 | #include <stdlib.h>
|
| 44 | #include <string.h>
|
| 45 | #include <unistd.h>
|
| 46 | #include <paths.h>
|
| 47 | #include <err.h>
|
| 48 |
|
| 49 | #include <arpa/inet.h>
|
| 50 | #include <arpa/nameser.h>
|
| 51 |
|
| 52 | #include <net/if.h>
|
| 53 | #include <net/ethernet.h>
|
| 54 | #include <netinet/ether.h>
|
| 55 | #include <netinet/in.h>
|
| 56 |
|
| 57 | enum {
|
| 58 | RV_OK = 0,
|
| 59 | RV_USAGE = 1,
|
| 60 | RV_NOTFOUND = 2,
|
| 61 | RV_NOENUM = 3
|
| 62 | };
|
| 63 |
|
| 64 | static int usage(const char *);
|
| 65 |
|
| 66 | static int parsenum(const char *word, unsigned long *result)
|
| 67 | {
|
| 68 | unsigned long num;
|
| 69 | char *ep;
|
| 70 |
|
| 71 | if (!isdigit((unsigned char)word[0]))
|
| 72 | return 0;
|
| 73 | errno = 0;
|
| 74 | num = strtoul(word, &ep, 10);
|
| 75 | if (num == ULONG_MAX && errno == ERANGE)
|
| 76 | return 0;
|
| 77 | if (*ep != '\0')
|
| 78 | return 0;
|
| 79 | *result = num;
|
| 80 | return 1;
|
| 81 | }
|
| 82 |
|
| 83 | |
| 84 | * printfmtstrings --
|
| 85 | * vprintf(format, ...),
|
| 86 | * then the aliases (beginning with prefix, separated by sep),
|
| 87 | * then a newline
|
| 88 |
|
| 89 | __attribute__ ((format (printf, 4, 5)))
|
| 90 | static void printfmtstrings(char *strings[], const char *prefix, const char *sep,
|
| 91 | const char *fmt, ...)
|
| 92 | {
|
| 93 | va_list ap;
|
| 94 | const char *curpref;
|
| 95 | size_t i;
|
| 96 |
|
| 97 | va_start(ap, fmt);
|
| 98 | (void)vprintf(fmt, ap);
|
| 99 | va_end(ap);
|
| 100 |
|
| 101 | curpref = prefix;
|
| 102 | for (i = 0; strings[i] != NULL; i++) {
|
| 103 | (void)printf("%s%s", curpref, strings[i]);
|
| 104 | curpref = sep;
|
| 105 | }
|
| 106 | (void)printf("\n");
|
| 107 | }
|
| 108 |
|
| 109 | static int ethers(int argc, char *argv[])
|
| 110 | {
|
| 111 | char hostname[MAXHOSTNAMELEN + 1], *hp;
|
| 112 | struct ether_addr ea, *eap;
|
| 113 | int i, rv;
|
| 114 |
|
| 115 | if (argc == 2) {
|
| 116 | warnx("Enumeration not supported on ethers");
|
| 117 | return RV_NOENUM;
|
| 118 | }
|
| 119 |
|
| 120 | rv = RV_OK;
|
| 121 | for (i = 2; i < argc; i++) {
|
| 122 | if ((eap = ether_aton(argv[i])) == NULL) {
|
| 123 | eap = &ea;
|
| 124 | hp = argv[i];
|
| 125 | if (ether_hostton(hp, eap) != 0) {
|
| 126 | rv = RV_NOTFOUND;
|
| 127 | break;
|
| 128 | }
|
| 129 | } else {
|
| 130 | hp = hostname;
|
| 131 | if (ether_ntohost(hp, eap) != 0) {
|
| 132 | rv = RV_NOTFOUND;
|
| 133 | break;
|
| 134 | }
|
| 135 | }
|
| 136 | (void)printf("%-17s %s\n", ether_ntoa(eap), hp);
|
| 137 | }
|
| 138 | return rv;
|
| 139 | }
|
| 140 |
|
| 141 | static void groupprint(const struct group *gr)
|
| 142 | {
|
| 143 | printfmtstrings(gr->gr_mem, "", ",", "%s:%s:%u:",
|
| 144 | gr->gr_name, gr->gr_passwd, gr->gr_gid);
|
| 145 | }
|
| 146 |
|
| 147 | static int group(int argc, char *argv[])
|
| 148 | {
|
| 149 | struct group *gr;
|
| 150 | unsigned long id;
|
| 151 | int i, rv;
|
| 152 |
|
| 153 | rv = RV_OK;
|
| 154 | if (argc == 2) {
|
| 155 | while ((gr = getgrent()) != NULL)
|
| 156 | groupprint(gr);
|
| 157 | } else {
|
| 158 | for (i = 2; i < argc; i++) {
|
| 159 | if (parsenum(argv[i], &id))
|
| 160 | gr = getgrgid((gid_t)id);
|
| 161 | else
|
| 162 | gr = getgrnam(argv[i]);
|
| 163 | if (gr == NULL) {
|
| 164 | rv = RV_NOTFOUND;
|
| 165 | break;
|
| 166 | }
|
| 167 | groupprint(gr);
|
| 168 | }
|
| 169 | }
|
| 170 | endgrent();
|
| 171 | return rv;
|
| 172 | }
|
| 173 |
|
| 174 | static void hostsprint(const struct hostent *he)
|
| 175 | {
|
| 176 | char buf[INET6_ADDRSTRLEN];
|
| 177 |
|
| 178 | if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL)
|
| 179 | (void)strlcpy(buf, "# unknown", sizeof(buf));
|
| 180 | printfmtstrings(he->h_aliases, " ", " ", "%-16s %s", buf, he->h_name);
|
| 181 | }
|
| 182 |
|
| 183 | static int hosts(int argc, char *argv[])
|
| 184 | {
|
| 185 | struct hostent *he;
|
| 186 | char addr[IN6ADDRSZ];
|
| 187 | int i, rv;
|
| 188 |
|
| 189 | sethostent(1);
|
| 190 | rv = RV_OK;
|
| 191 | if (argc == 2) {
|
| 192 | while ((he = gethostent()) != NULL)
|
| 193 | hostsprint(he);
|
| 194 | } else {
|
| 195 | for (i = 2; i < argc; i++) {
|
| 196 | if (inet_pton(AF_INET6, argv[i], (void *)addr) > 0)
|
| 197 | he = gethostbyaddr(addr, IN6ADDRSZ, AF_INET6);
|
| 198 | else if (inet_pton(AF_INET, argv[i], (void *)addr) > 0)
|
| 199 | he = gethostbyaddr(addr, INADDRSZ, AF_INET);
|
| 200 | else if ((he = gethostbyname2(argv[i], AF_INET6)) == NULL)
|
| 201 | he = gethostbyname2(argv[i], AF_INET);
|
| 202 | if (he == NULL) {
|
| 203 | rv = RV_NOTFOUND;
|
| 204 | break;
|
| 205 | }
|
| 206 | hostsprint(he);
|
| 207 | }
|
| 208 | }
|
| 209 | endhostent();
|
| 210 | return rv;
|
| 211 | }
|
| 212 |
|
| 213 | static int ahosts_ex(int family, int flags, int argc, char *argv[])
|
| 214 | {
|
| 215 | static const char *socktypes[] = {
|
| 216 | [SOCK_STREAM] = "STREAM",
|
| 217 | [SOCK_DGRAM] = "DGRAM",
|
| 218 | [SOCK_RAW] = "RAW",
|
| 219 | [SOCK_RDM] = "RDM",
|
| 220 | [SOCK_SEQPACKET] = "SEQPACKET",
|
| 221 | [SOCK_DCCP] = "DCCP",
|
| 222 | [SOCK_PACKET] = "PACKET",
|
| 223 | };
|
| 224 | const char *sockstr;
|
| 225 | char sockbuf[16], buf[INET6_ADDRSTRLEN];
|
| 226 | struct addrinfo *res, *r, hint;
|
| 227 | void *addr;
|
| 228 | int i;
|
| 229 |
|
| 230 | if (argc == 2)
|
| 231 | return hosts(argc, argv);
|
| 232 |
|
| 233 | hint = (struct addrinfo) {
|
| 234 | .ai_family = family,
|
| 235 | .ai_flags = AI_ADDRCONFIG | AI_CANONNAME | flags,
|
| 236 | };
|
| 237 |
|
| 238 | for (i = 2; i < argc; i++) {
|
| 239 | if (getaddrinfo(argv[i], 0, &hint, &res) != 0)
|
| 240 | return RV_NOTFOUND;
|
| 241 |
|
| 242 | for (r = res; r; r = r->ai_next) {
|
| 243 | sockstr = NULL;
|
| 244 | if (r->ai_socktype >= 0 && r->ai_socktype < sizeof(socktypes)/sizeof(socktypes[0]))
|
| 245 | sockstr = socktypes[r->ai_socktype];
|
| 246 | if (!sockstr) {
|
| 247 | sprintf(buf, "%d", r->ai_socktype);
|
| 248 | sockstr = sockbuf;
|
| 249 | }
|
| 250 | switch (r->ai_family) {
|
| 251 | case AF_INET:
|
| 252 | addr = &((struct sockaddr_in*) r->ai_addr)->sin_addr;
|
| 253 | break;
|
| 254 | case AF_INET6:
|
| 255 | addr = &((struct sockaddr_in6*) r->ai_addr)->sin6_addr;
|
| 256 | break;
|
| 257 | default:
|
| 258 | continue;
|
| 259 | }
|
| 260 | if (inet_ntop(r->ai_family, addr, buf, sizeof(buf)) == NULL)
|
| 261 | (void)strlcpy(buf, "# unknown", sizeof(buf));
|
| 262 | printf("%-15s %-6s %s\n", buf, sockstr, r->ai_canonname ?: "");
|
| 263 | }
|
| 264 | }
|
| 265 |
|
| 266 | return RV_OK;
|
| 267 | }
|
| 268 |
|
| 269 | static int ahosts(int argc, char *argv[])
|
| 270 | {
|
| 271 | return ahosts_ex(AF_UNSPEC, 0, argc, argv);
|
| 272 | }
|
| 273 |
|
| 274 | static int ahostsv4(int argc, char *argv[])
|
| 275 | {
|
| 276 | return ahosts_ex(AF_INET, 0, argc, argv);
|
| 277 | }
|
| 278 |
|
| 279 | static int ahostsv6(int argc, char *argv[])
|
| 280 | {
|
| 281 | return ahosts_ex(AF_INET6, AI_V4MAPPED, argc, argv);
|
| 282 | }
|
| 283 |
|
| 284 | static void networksprint(const struct netent *ne)
|
| 285 | {
|
| 286 | char buf[INET6_ADDRSTRLEN];
|
| 287 | struct in_addr ianet;
|
| 288 |
|
| 289 | ianet = inet_makeaddr(ne->n_net, 0);
|
| 290 | if (inet_ntop(ne->n_addrtype, &ianet, buf, sizeof(buf)) == NULL)
|
| 291 | (void)strlcpy(buf, "# unknown", sizeof(buf));
|
| 292 | printfmtstrings(ne->n_aliases, " ", " ", "%-16s %s", ne->n_name, buf);
|
| 293 | }
|
| 294 |
|
| 295 | static int networks(int argc, char *argv[])
|
| 296 | {
|
| 297 | struct netent *ne;
|
| 298 | in_addr_t net;
|
| 299 | int i, rv;
|
| 300 |
|
| 301 | setnetent(1);
|
| 302 | rv = RV_OK;
|
| 303 | if (argc == 2) {
|
| 304 | while ((ne = getnetent()) != NULL)
|
| 305 | networksprint(ne);
|
| 306 | } else {
|
| 307 | for (i = 2; i < argc; i++) {
|
| 308 | net = inet_network(argv[i]);
|
| 309 | if (net != INADDR_NONE)
|
| 310 | ne = getnetbyaddr(net, AF_INET);
|
| 311 | else
|
| 312 | ne = getnetbyname(argv[i]);
|
| 313 | if (ne == NULL) {
|
| 314 | rv = RV_NOTFOUND;
|
| 315 | break;
|
| 316 | }
|
| 317 | networksprint(ne);
|
| 318 | }
|
| 319 | }
|
| 320 | endnetent();
|
| 321 | return rv;
|
| 322 | }
|
| 323 |
|
| 324 | static void passwdprint(struct passwd *pw)
|
| 325 | {
|
| 326 | (void)printf("%s:%s:%u:%u:%s:%s:%s\n",
|
| 327 | pw->pw_name, pw->pw_passwd, pw->pw_uid,
|
| 328 | pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell);
|
| 329 | }
|
| 330 |
|
| 331 | static int passwd(int argc, char *argv[])
|
| 332 | {
|
| 333 | struct passwd *pw;
|
| 334 | unsigned long id;
|
| 335 | int i, rv;
|
| 336 |
|
| 337 | rv = RV_OK;
|
| 338 | if (argc == 2) {
|
| 339 | while ((pw = getpwent()) != NULL)
|
| 340 | passwdprint(pw);
|
| 341 | } else {
|
| 342 | for (i = 2; i < argc; i++) {
|
| 343 | if (parsenum(argv[i], &id))
|
| 344 | pw = getpwuid((uid_t)id);
|
| 345 | else
|
| 346 | pw = getpwnam(argv[i]);
|
| 347 | if (pw == NULL) {
|
| 348 | rv = RV_NOTFOUND;
|
| 349 | break;
|
| 350 | }
|
| 351 | passwdprint(pw);
|
| 352 | }
|
| 353 | }
|
| 354 | endpwent();
|
| 355 | return rv;
|
| 356 | }
|
| 357 |
|
| 358 | static void protocolsprint(struct protoent *pe)
|
| 359 | {
|
| 360 | printfmtstrings(pe->p_aliases, " ", " ",
|
| 361 | "%-16s %5d", pe->p_name, pe->p_proto);
|
| 362 | }
|
| 363 |
|
| 364 | static int protocols(int argc, char *argv[])
|
| 365 | {
|
| 366 | struct protoent *pe;
|
| 367 | unsigned long id;
|
| 368 | int i, rv;
|
| 369 |
|
| 370 | setprotoent(1);
|
| 371 | rv = RV_OK;
|
| 372 | if (argc == 2) {
|
| 373 | while ((pe = getprotoent()) != NULL)
|
| 374 | protocolsprint(pe);
|
| 375 | } else {
|
| 376 | for (i = 2; i < argc; i++) {
|
| 377 | if (parsenum(argv[i], &id))
|
| 378 | pe = getprotobynumber((int)id);
|
| 379 | else
|
| 380 | pe = getprotobyname(argv[i]);
|
| 381 | if (pe == NULL) {
|
| 382 | rv = RV_NOTFOUND;
|
| 383 | break;
|
| 384 | }
|
| 385 | protocolsprint(pe);
|
| 386 | }
|
| 387 | }
|
| 388 | endprotoent();
|
| 389 | return rv;
|
| 390 | }
|
| 391 |
|
| 392 | static void servicesprint(struct servent *se)
|
| 393 | {
|
| 394 | printfmtstrings(se->s_aliases, " ", " ",
|
| 395 | "%-16s %5d/%s",
|
| 396 | se->s_name, ntohs(se->s_port), se->s_proto);
|
| 397 |
|
| 398 | }
|
| 399 |
|
| 400 | static int services(int argc, char *argv[])
|
| 401 | {
|
| 402 | struct servent *se;
|
| 403 | unsigned long id;
|
| 404 | char *proto;
|
| 405 | int i, rv;
|
| 406 |
|
| 407 | setservent(1);
|
| 408 | rv = RV_OK;
|
| 409 | if (argc == 2) {
|
| 410 | while ((se = getservent()) != NULL)
|
| 411 | servicesprint(se);
|
| 412 | } else {
|
| 413 | for (i = 2; i < argc; i++) {
|
| 414 | proto = strchr(argv[i], '/');
|
| 415 | if (proto != NULL)
|
| 416 | *proto++ = '\0';
|
| 417 | if (parsenum(argv[i], &id))
|
| 418 | se = getservbyport(htons(id), proto);
|
| 419 | else
|
| 420 | se = getservbyname(argv[i], proto);
|
| 421 | if (se == NULL) {
|
| 422 | rv = RV_NOTFOUND;
|
| 423 | break;
|
| 424 | }
|
| 425 | servicesprint(se);
|
| 426 | }
|
| 427 | }
|
| 428 | endservent();
|
| 429 | return rv;
|
| 430 | }
|
| 431 |
|
| 432 | static int shadow(int argc, char *argv[])
|
| 433 | {
|
| 434 | struct spwd *sp;
|
| 435 | int i, rv;
|
| 436 |
|
| 437 | rv = RV_OK;
|
| 438 | if (argc == 2) {
|
| 439 | while ((sp = getspent()) != NULL)
|
| 440 | putspent(sp, stdout);
|
| 441 | } else {
|
| 442 | for (i = 2; i < argc; i++) {
|
| 443 | sp = getspnam(argv[i]);
|
| 444 | if (sp == NULL) {
|
| 445 | rv = RV_NOTFOUND;
|
| 446 | break;
|
| 447 | }
|
| 448 | putspent(sp, stdout);
|
| 449 | }
|
| 450 | }
|
| 451 | endspent();
|
| 452 | return rv;
|
| 453 | }
|
| 454 |
|
| 455 | static int shells(int argc, char *argv[])
|
| 456 | {
|
| 457 | const char *sh;
|
| 458 | int i, rv;
|
| 459 |
|
| 460 | setusershell();
|
| 461 | rv = RV_OK;
|
| 462 | if (argc == 2) {
|
| 463 | while ((sh = getusershell()) != NULL)
|
| 464 | (void)printf("%s\n", sh);
|
| 465 | } else {
|
| 466 | for (i = 2; i < argc; i++) {
|
| 467 | setusershell();
|
| 468 | while ((sh = getusershell()) != NULL) {
|
| 469 | if (strcmp(sh, argv[i]) == 0) {
|
| 470 | (void)printf("%s\n", sh);
|
| 471 | break;
|
| 472 | }
|
| 473 | }
|
| 474 | if (sh == NULL) {
|
| 475 | rv = RV_NOTFOUND;
|
| 476 | break;
|
| 477 | }
|
| 478 | }
|
| 479 | }
|
| 480 | endusershell();
|
| 481 | return rv;
|
| 482 | }
|
| 483 |
|
| 484 | static struct getentdb {
|
| 485 | const char *name;
|
| 486 | int (*callback)(int, char *[]);
|
| 487 | } databases[] = {
|
| 488 | { "ethers", ethers, },
|
| 489 | { "group", group, },
|
| 490 | { "hosts", hosts, },
|
| 491 | { "ahosts", ahosts, },
|
| 492 | { "ahostsv4", ahostsv4, },
|
| 493 | { "ahostsv6", ahostsv6, },
|
| 494 | { "networks", networks, },
|
| 495 | { "passwd", passwd, },
|
| 496 | { "protocols", protocols, },
|
| 497 | { "services", services, },
|
| 498 | { "shadow", shadow, },
|
| 499 | { "shells", shells, },
|
| 500 |
|
| 501 | { NULL, NULL, },
|
| 502 | };
|
| 503 |
|
| 504 | static int usage(const char *arg0)
|
| 505 | {
|
| 506 | struct getentdb *curdb;
|
| 507 | size_t i;
|
| 508 |
|
| 509 | (void)fprintf(stderr, "Usage: %s database [key ...]\n", arg0);
|
| 510 | (void)fprintf(stderr, "\tdatabase may be one of:");
|
| 511 | for (i = 0, curdb = databases; curdb->name != NULL; curdb++, i++) {
|
| 512 | if (i % 7 == 0)
|
| 513 | (void)fputs("\n\t\t", stderr);
|
| 514 | (void)fprintf(stderr, "%s%s", i % 7 == 0 ? "" : " ",
|
| 515 | curdb->name);
|
| 516 | }
|
| 517 | (void)fprintf(stderr, "\n");
|
| 518 | exit(RV_USAGE);
|
| 519 |
|
| 520 | }
|
| 521 |
|
| 522 | int
|
| 523 | main(int argc, char *argv[])
|
| 524 | {
|
| 525 | struct getentdb *curdb;
|
| 526 |
|
| 527 | if (argc < 2)
|
| 528 | usage(argv[0]);
|
| 529 | for (curdb = databases; curdb->name != NULL; curdb++)
|
| 530 | if (strcmp(curdb->name, argv[1]) == 0)
|
| 531 | return (*curdb->callback)(argc, argv);
|
| 532 |
|
| 533 | warn("Unknown database `%s'", argv[1]);
|
| 534 | usage(argv[0]);
|
| 535 |
|
| 536 | }
|