1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 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 #ifndef lint 33 static const char copyright[] = 34 "@(#) Copyright (c) 1983, 1993\n\ 35 The Regents of the University of California. All rights reserved.\n"; 36 #endif /* not lint */ 37 38 #if 0 39 #ifndef lint 40 static char sccsid[] = "@(#)logger.c 8.1 (Berkeley) 6/6/93"; 41 #endif /* not lint */ 42 #endif 43 44 #include <sys/cdefs.h> 45 __FBSDID("$FreeBSD$"); 46 47 #include <sys/capsicum.h> 48 #include <sys/param.h> 49 #include <sys/socket.h> 50 #include <netinet/in.h> 51 52 #include <capsicum_helpers.h> 53 #include <ctype.h> 54 #include <err.h> 55 #include <netdb.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <time.h> 60 #include <unistd.h> 61 62 #include <libcasper.h> 63 #include <casper/cap_syslog.h> 64 65 #define SYSLOG_NAMES 66 #include <syslog.h> 67 68 #define sstosa(ss) ((struct sockaddr *)(void *)ss) 69 70 struct socks { 71 int sk_sock; 72 int sk_addrlen; 73 struct sockaddr_storage sk_addr; 74 }; 75 76 static int decode(char *, const CODE *); 77 static int pencode(char *); 78 static ssize_t socksetup(const char *, const char *, const char *, 79 struct socks **); 80 static void logmessage(int, const char *, const char *, const char *, 81 struct socks *, ssize_t, const char *); 82 static void usage(void); 83 84 static cap_channel_t *capsyslog; 85 #ifdef INET6 86 static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ 87 #else 88 static int family = PF_INET; /* protocol family (IPv4 only) */ 89 #endif 90 static int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ 91 92 /* 93 * logger -- read and log utility 94 * 95 * Reads from an input and arranges to write the result on the system 96 * log. 97 */ 98 int 99 main(int argc, char *argv[]) 100 { 101 cap_channel_t *capcas; 102 struct socks *socks; 103 ssize_t nsock; 104 time_t now; 105 int ch, logflags, pri; 106 char *tag, *host, buf[1024], *timestamp, tbuf[26], 107 *hostname, hbuf[MAXHOSTNAMELEN], *pristr; 108 const char *svcname, *src; 109 110 tag = NULL; 111 host = NULL; 112 hostname = NULL; 113 svcname = "syslog"; 114 src = NULL; 115 socks = NULL; 116 pri = LOG_USER | LOG_NOTICE; 117 pristr = NULL; 118 logflags = 0; 119 unsetenv("TZ"); 120 while ((ch = getopt(argc, argv, "46Af:H:h:iP:p:S:st:")) != -1) 121 switch((char)ch) { 122 case '4': 123 family = PF_INET; 124 break; 125 #ifdef INET6 126 case '6': 127 family = PF_INET6; 128 break; 129 #endif 130 case 'A': 131 send_to_all++; 132 break; 133 case 'f': /* file to log */ 134 if (freopen(optarg, "r", stdin) == NULL) 135 err(1, "%s", optarg); 136 setvbuf(stdin, 0, _IONBF, 0); 137 break; 138 case 'H': /* hostname to set in message header */ 139 hostname = optarg; 140 break; 141 case 'h': /* hostname to deliver to */ 142 host = optarg; 143 break; 144 case 'i': /* log process id also */ 145 logflags |= LOG_PID; 146 break; 147 case 'P': /* service name or port number */ 148 svcname = optarg; 149 break; 150 case 'p': /* priority */ 151 pristr = optarg; 152 break; 153 case 's': /* log to standard error */ 154 logflags |= LOG_PERROR; 155 break; 156 case 'S': /* source address */ 157 src = optarg; 158 break; 159 case 't': /* tag */ 160 tag = optarg; 161 break; 162 case '?': 163 default: 164 usage(); 165 } 166 argc -= optind; 167 argv += optind; 168 169 if (host) { 170 nsock = socksetup(src, host, svcname, &socks); 171 if (nsock <= 0) 172 errx(1, "socket"); 173 } else { 174 if (src) 175 errx(1, "-h option is missing."); 176 nsock = 0; 177 } 178 179 capcas = cap_init(); 180 if (capcas == NULL) 181 err(1, "Unable to contact Casper"); 182 caph_cache_catpages(); 183 caph_cache_tzdata(); 184 if (nsock == 0) { 185 if (caph_enter_casper() < 0) 186 err(1, "Unable to enter capability mode"); 187 } 188 capsyslog = cap_service_open(capcas, "system.syslog"); 189 if (capsyslog == NULL) 190 err(1, "Unable to open system.syslog service"); 191 cap_close(capcas); 192 193 if (pristr != NULL) 194 pri = pencode(pristr); 195 if (tag == NULL) 196 tag = getlogin(); 197 /* setup for logging */ 198 if (host == NULL) 199 cap_openlog(capsyslog, tag, logflags, 0); 200 201 (void )time(&now); 202 (void )ctime_r(&now, tbuf); 203 tbuf[19] = '\0'; 204 timestamp = tbuf + 4; 205 206 if (hostname == NULL) { 207 hostname = hbuf; 208 (void )gethostname(hbuf, MAXHOSTNAMELEN); 209 *strchrnul(hostname, '.') = '\0'; 210 } 211 212 /* log input line if appropriate */ 213 if (argc > 0) { 214 char *p, *endp; 215 size_t len; 216 217 for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) { 218 len = strlen(*argv); 219 if (p + len > endp && p > buf) { 220 logmessage(pri, timestamp, hostname, tag, 221 socks, nsock, buf); 222 p = buf; 223 } 224 if (len > sizeof(buf) - 1) 225 logmessage(pri, timestamp, hostname, tag, 226 socks, nsock, *argv++); 227 else { 228 if (p != buf) 229 *p++ = ' '; 230 bcopy(*argv++, p, len); 231 *(p += len) = '\0'; 232 } 233 } 234 if (p != buf) 235 logmessage(pri, timestamp, hostname, tag, socks, nsock, 236 buf); 237 } else 238 while (fgets(buf, sizeof(buf), stdin) != NULL) 239 logmessage(pri, timestamp, hostname, tag, socks, nsock, 240 buf); 241 exit(0); 242 } 243 244 static ssize_t 245 socksetup(const char *src, const char *dst, const char *svcname, 246 struct socks **socks) 247 { 248 struct addrinfo hints, *res, *res0; 249 struct sockaddr_storage *ss_src[AF_MAX]; 250 struct socks *sk; 251 ssize_t nsock = 0; 252 int error, maxs; 253 254 memset(&ss_src[0], 0, sizeof(ss_src)); 255 if (src) { 256 char *p, *p0, *hs, *hbuf, *sbuf; 257 258 hbuf = sbuf = NULL; 259 p0 = p = strdup(src); 260 if (p0 == NULL) 261 err(1, "strdup failed"); 262 hs = p0; /* point to search ":" */ 263 #ifdef INET6 264 /* -S option supports IPv6 addr in "[2001:db8::1]:service". */ 265 if (*p0 == '[') { 266 p = strchr(p0, ']'); 267 if (p == NULL) 268 errx(1, "\"]\" not found in src addr"); 269 *p = '\0'; 270 /* hs points just after ']' (':' or '\0'). */ 271 hs = p + 1; 272 /* 273 * p points just after '[' while it points hs 274 * in the case of []. 275 */ 276 p = ((p0 + 1) == (hs - 1)) ? hs : p0 + 1; 277 } 278 #endif 279 if (*p != '\0') { 280 /* (p == hs) means ":514" or "[]:514". */ 281 hbuf = (p == hs && *p == ':') ? NULL : p; 282 p = strchr(hs, ':'); 283 if (p != NULL) { 284 *p = '\0'; 285 sbuf = (*(p + 1) != '\0') ? p + 1 : NULL; 286 } 287 } 288 hints = (struct addrinfo){ 289 .ai_family = family, 290 .ai_socktype = SOCK_DGRAM, 291 .ai_flags = AI_PASSIVE 292 }; 293 error = getaddrinfo(hbuf, sbuf, &hints, &res0); 294 if (error) 295 errx(1, "%s: %s", gai_strerror(error), src); 296 for (res = res0; res; res = res->ai_next) { 297 switch (res->ai_family) { 298 case AF_INET: 299 #ifdef INET6 300 case AF_INET6: 301 #endif 302 if (ss_src[res->ai_family] != NULL) 303 continue; 304 ss_src[res->ai_family] = 305 malloc(sizeof(struct sockaddr_storage)); 306 if (ss_src[res->ai_family] == NULL) 307 err(1, "malloc failed"); 308 memcpy(ss_src[res->ai_family], res->ai_addr, 309 res->ai_addrlen); 310 } 311 } 312 freeaddrinfo(res0); 313 free(p0); 314 } 315 316 /* resolve hostname */ 317 hints = (struct addrinfo){ 318 .ai_family = family, 319 .ai_socktype = SOCK_DGRAM 320 }; 321 error = getaddrinfo(dst, svcname, &hints, &res0); 322 if (error == EAI_SERVICE) { 323 warnx("%s/udp: unknown service", svcname); 324 error = getaddrinfo(dst, "514", &hints, &res0); 325 } 326 if (error) 327 errx(1, "%s: %s", gai_strerror(error), dst); 328 /* count max number of sockets we may open */ 329 maxs = 0; 330 for (res = res0; res; res = res->ai_next) 331 maxs++; 332 sk = calloc(maxs, sizeof(*sk)); 333 if (sk == NULL) 334 errx(1, "couldn't allocate memory for sockets"); 335 for (res = res0; res; res = res->ai_next) { 336 int s; 337 338 s = socket(res->ai_family, res->ai_socktype, 339 res->ai_protocol); 340 if (s < 0) 341 continue; 342 if (src && ss_src[res->ai_family] == NULL) 343 errx(1, "address family mismatch"); 344 345 if (ss_src[res->ai_family]) { 346 error = bind(s, sstosa(ss_src[res->ai_family]), 347 ss_src[res->ai_family]->ss_len); 348 if (error < 0) 349 err(1, "bind"); 350 } 351 sk[nsock] = (struct socks){ 352 .sk_addrlen = res->ai_addrlen, 353 .sk_sock = s 354 }; 355 memcpy(&sk[nsock].sk_addr, res->ai_addr, res->ai_addrlen); 356 nsock++; 357 } 358 freeaddrinfo(res0); 359 360 *socks = sk; 361 return (nsock); 362 } 363 364 /* 365 * Send the message to syslog, either on the local host, or on a remote host 366 */ 367 static void 368 logmessage(int pri, const char *timestamp, const char *hostname, 369 const char *tag, struct socks *sk, ssize_t nsock, const char *buf) 370 { 371 char *line; 372 int len, i, lsent; 373 374 if (nsock == 0) { 375 cap_syslog(capsyslog, pri, "%s", buf); 376 return; 377 } 378 if ((len = asprintf(&line, "<%d>%s %s %s: %s", pri, timestamp, 379 hostname, tag, buf)) == -1) 380 errx(1, "asprintf"); 381 382 lsent = -1; 383 for (i = 0; i < nsock; i++) { 384 lsent = sendto(sk[i].sk_sock, line, len, 0, 385 sstosa(&sk[i].sk_addr), sk[i].sk_addrlen); 386 if (lsent == len && !send_to_all) 387 break; 388 } 389 if (lsent != len) { 390 if (lsent == -1) 391 warn("sendto"); 392 else 393 warnx("sendto: short send - %d bytes", lsent); 394 } 395 396 free(line); 397 } 398 399 /* 400 * Decode a symbolic name to a numeric value 401 */ 402 static int 403 pencode(char *s) 404 { 405 char *save; 406 int fac, lev; 407 408 for (save = s; *s && *s != '.'; ++s); 409 if (*s) { 410 *s = '\0'; 411 fac = decode(save, facilitynames); 412 if (fac < 0) 413 errx(1, "unknown facility name: %s", save); 414 *s++ = '.'; 415 } 416 else { 417 fac = 0; 418 s = save; 419 } 420 lev = decode(s, prioritynames); 421 if (lev < 0) 422 errx(1, "unknown priority name: %s", save); 423 return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); 424 } 425 426 static int 427 decode(char *name, const CODE *codetab) 428 { 429 const CODE *c; 430 431 if (isdigit(*name)) 432 return (atoi(name)); 433 434 for (c = codetab; c->c_name; c++) 435 if (!strcasecmp(name, c->c_name)) 436 return (c->c_val); 437 438 return (-1); 439 } 440 441 static void 442 usage(void) 443 { 444 (void)fprintf(stderr, "usage: %s\n", 445 "logger [-46Ais] [-f file] [-h host] [-P port] [-p pri] [-t tag]\n" 446 " [-S addr:port] [message ...]" 447 ); 448 exit(1); 449 } 450