1 /* 2 * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1996, 1998-2001, 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #if !defined(LINT) && !defined(CODECENTER) 19 static const char rcsid[] = "$Id: irp.c,v 1.12 2008/11/14 02:36:51 marka Exp $"; 20 #endif 21 22 /* Imports */ 23 24 #include "port_before.h" 25 26 #include <syslog.h> 27 #include <sys/types.h> 28 #include <sys/socket.h> 29 #include <sys/un.h> 30 #include <netinet/in.h> 31 #include <arpa/inet.h> 32 #include <stdlib.h> 33 #include <errno.h> 34 #include <string.h> 35 #include <stdarg.h> 36 #include <fcntl.h> 37 #include <syslog.h> 38 #include <ctype.h> 39 #include <unistd.h> 40 41 #include <isc/memcluster.h> 42 43 #include <irs.h> 44 #include <irp.h> 45 46 #include "irs_p.h" 47 #include "irp_p.h" 48 49 #include "port_after.h" 50 51 /* Forward. */ 52 53 static void irp_close(struct irs_acc *); 54 55 #define LINEINCR 128 56 57 #if !defined(SUN_LEN) 58 #define SUN_LEN(su) \ 59 (sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path)) 60 #endif 61 62 63 /* Public */ 64 65 66 /* send errors to syslog if true. */ 67 int irp_log_errors = 1; 68 69 /*% 70 * This module handles the irp module connection to irpd. 71 * 72 * The client expects a synchronous interface to functions like 73 * getpwnam(3), so we can't use the ctl_* i/o library on this end of 74 * the wire (it's used in the server). 75 */ 76 77 /*% 78 * irs_acc *irs_irp_acc(const char *options); 79 * 80 * Initialize the irp module. 81 */ 82 struct irs_acc * 83 irs_irp_acc(const char *options) { 84 struct irs_acc *acc; 85 struct irp_p *irp; 86 87 UNUSED(options); 88 89 if (!(acc = memget(sizeof *acc))) { 90 errno = ENOMEM; 91 return (NULL); 92 } 93 memset(acc, 0x5e, sizeof *acc); 94 if (!(irp = memget(sizeof *irp))) { 95 errno = ENOMEM; 96 free(acc); 97 return (NULL); 98 } 99 irp->inlast = 0; 100 irp->incurr = 0; 101 irp->fdCxn = -1; 102 acc->private = irp; 103 104 #ifdef WANT_IRS_GR 105 acc->gr_map = irs_irp_gr; 106 #else 107 acc->gr_map = NULL; 108 #endif 109 #ifdef WANT_IRS_PW 110 acc->pw_map = irs_irp_pw; 111 #else 112 acc->pw_map = NULL; 113 #endif 114 acc->sv_map = irs_irp_sv; 115 acc->pr_map = irs_irp_pr; 116 acc->ho_map = irs_irp_ho; 117 acc->nw_map = irs_irp_nw; 118 acc->ng_map = irs_irp_ng; 119 acc->close = irp_close; 120 return (acc); 121 } 122 123 124 int 125 irs_irp_connection_setup(struct irp_p *cxndata, int *warned) { 126 if (irs_irp_is_connected(cxndata)) { 127 return (0); 128 } else if (irs_irp_connect(cxndata) != 0) { 129 if (warned != NULL && !*warned) { 130 syslog(LOG_ERR, "irpd connection failed: %m\n"); 131 (*warned)++; 132 } 133 134 return (-1); 135 } 136 137 return (0); 138 } 139 140 /*% 141 * int irs_irp_connect(void); 142 * 143 * Sets up the connection to the remote irpd server. 144 * 145 * Returns: 146 * 147 * 0 on success, -1 on failure. 148 * 149 */ 150 int 151 irs_irp_connect(struct irp_p *pvt) { 152 int flags; 153 struct sockaddr *addr; 154 struct sockaddr_in iaddr; 155 #ifndef NO_SOCKADDR_UN 156 struct sockaddr_un uaddr; 157 #endif 158 long ipaddr; 159 const char *irphost; 160 int code; 161 char text[256]; 162 int socklen = 0; 163 164 if (pvt->fdCxn != -1) { 165 perror("fd != 1"); 166 return (-1); 167 } 168 169 #ifndef NO_SOCKADDR_UN 170 memset(&uaddr, 0, sizeof uaddr); 171 #endif 172 memset(&iaddr, 0, sizeof iaddr); 173 174 irphost = getenv(IRPD_HOST_ENV); 175 if (irphost == NULL) { 176 irphost = "127.0.0.1"; 177 } 178 179 #ifndef NO_SOCKADDR_UN 180 if (irphost[0] == '/') { 181 addr = (struct sockaddr *)&uaddr; 182 strncpy(uaddr.sun_path, irphost, sizeof uaddr.sun_path); 183 uaddr.sun_family = AF_UNIX; 184 socklen = SUN_LEN(&uaddr); 185 #ifdef HAVE_SA_LEN 186 uaddr.sun_len = socklen; 187 #endif 188 } else 189 #endif 190 { 191 if (inet_pton(AF_INET, irphost, &ipaddr) != 1) { 192 errno = EADDRNOTAVAIL; 193 perror("inet_pton"); 194 return (-1); 195 } 196 197 addr = (struct sockaddr *)&iaddr; 198 socklen = sizeof iaddr; 199 #ifdef HAVE_SA_LEN 200 iaddr.sin_len = socklen; 201 #endif 202 iaddr.sin_family = AF_INET; 203 iaddr.sin_port = htons(IRPD_PORT); 204 iaddr.sin_addr.s_addr = ipaddr; 205 } 206 207 208 pvt->fdCxn = socket(addr->sa_family, SOCK_STREAM, PF_UNSPEC); 209 if (pvt->fdCxn < 0) { 210 perror("socket"); 211 return (-1); 212 } 213 214 if (connect(pvt->fdCxn, addr, socklen) != 0) { 215 perror("connect"); 216 return (-1); 217 } 218 219 flags = fcntl(pvt->fdCxn, F_GETFL, 0); 220 if (flags < 0) { 221 close(pvt->fdCxn); 222 perror("close"); 223 return (-1); 224 } 225 226 #if 0 227 flags |= O_NONBLOCK; 228 if (fcntl(pvt->fdCxn, F_SETFL, flags) < 0) { 229 close(pvt->fdCxn); 230 perror("fcntl"); 231 return (-1); 232 } 233 #endif 234 235 code = irs_irp_read_response(pvt, text, sizeof text); 236 if (code != IRPD_WELCOME_CODE) { 237 if (irp_log_errors) { 238 syslog(LOG_WARNING, "Connection failed: %s", text); 239 } 240 irs_irp_disconnect(pvt); 241 return (-1); 242 } 243 244 return (0); 245 } 246 247 /*% 248 * int irs_irp_is_connected(struct irp_p *pvt); 249 * 250 * Returns: 251 * 252 * Non-zero if streams are setup to remote. 253 * 254 */ 255 256 int 257 irs_irp_is_connected(struct irp_p *pvt) { 258 return (pvt->fdCxn >= 0); 259 } 260 261 /*% 262 * void 263 * irs_irp_disconnect(struct irp_p *pvt); 264 * 265 * Closes streams to remote. 266 */ 267 268 void 269 irs_irp_disconnect(struct irp_p *pvt) { 270 if (pvt->fdCxn != -1) { 271 close(pvt->fdCxn); 272 pvt->fdCxn = -1; 273 } 274 } 275 276 277 278 int 279 irs_irp_read_line(struct irp_p *pvt, char *buffer, int len) { 280 char *realstart = &pvt->inbuffer[0]; 281 char *p, *start, *end; 282 int spare; 283 int i; 284 int buffpos = 0; 285 int left = len - 1; 286 287 while (left > 0) { 288 start = p = &pvt->inbuffer[pvt->incurr]; 289 end = &pvt->inbuffer[pvt->inlast]; 290 291 while (p != end && *p != '\n') 292 p++; 293 294 if (p == end) { 295 /* Found no newline so shift data down if necessary 296 * and append new data to buffer 297 */ 298 if (start > realstart) { 299 memmove(realstart, start, end - start); 300 pvt->inlast = end - start; 301 start = realstart; 302 pvt->incurr = 0; 303 end = &pvt->inbuffer[pvt->inlast]; 304 } 305 306 spare = sizeof (pvt->inbuffer) - pvt->inlast; 307 308 p = end; 309 i = read(pvt->fdCxn, end, spare); 310 if (i < 0) { 311 close(pvt->fdCxn); 312 pvt->fdCxn = -1; 313 return (buffpos > 0 ? buffpos : -1); 314 } else if (i == 0) { 315 return (buffpos); 316 } 317 318 end += i; 319 pvt->inlast += i; 320 321 while (p != end && *p != '\n') 322 p++; 323 } 324 325 if (p == end) { 326 /* full buffer and still no newline */ 327 i = sizeof pvt->inbuffer; 328 } else { 329 /* include newline */ 330 i = p - start + 1; 331 } 332 333 if (i > left) 334 i = left; 335 memcpy(buffer + buffpos, start, i); 336 pvt->incurr += i; 337 buffpos += i; 338 buffer[buffpos] = '\0'; 339 340 if (p != end) { 341 left = 0; 342 } else { 343 left -= i; 344 } 345 } 346 347 #if 0 348 fprintf(stderr, "read line: %s\n", buffer); 349 #endif 350 return (buffpos); 351 } 352 353 /*% 354 * int irp_read_response(struct irp_p *pvt); 355 * 356 * Returns: 357 * 358 * The number found at the beginning of the line read from 359 * FP. 0 on failure(0 is not a legal response code). The 360 * rest of the line is discarded. 361 * 362 */ 363 364 int 365 irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen) { 366 char line[1024]; 367 int code; 368 char *p; 369 370 if (irs_irp_read_line(pvt, line, sizeof line) <= 0) { 371 return (0); 372 } 373 374 p = strchr(line, '\n'); 375 if (p == NULL) { 376 return (0); 377 } 378 379 if (sscanf(line, "%d", &code) != 1) { 380 code = 0; 381 } else if (text != NULL && textlen > 0U) { 382 p = line; 383 while (isspace((unsigned char)*p)) p++; 384 while (isdigit((unsigned char)*p)) p++; 385 while (isspace((unsigned char)*p)) p++; 386 strncpy(text, p, textlen - 1); 387 p[textlen - 1] = '\0'; 388 } 389 390 return (code); 391 } 392 393 /*% 394 * char *irp_read_body(struct irp_p *pvt, size_t *size); 395 * 396 * Read in the body of a response. Terminated by a line with 397 * just a dot on it. Lines should be terminated with a CR-LF 398 * sequence, but we're nt piccky if the CR is missing. 399 * No leading dot escaping is done as the protcol doesn't 400 * use leading dots anywhere. 401 * 402 * Returns: 403 * 404 * Pointer to null-terminated buffer allocated by memget. 405 * *SIZE is set to the length of the buffer. 406 * 407 */ 408 409 char * 410 irs_irp_read_body(struct irp_p *pvt, size_t *size) { 411 char line[1024]; 412 u_int linelen; 413 size_t len = LINEINCR; 414 char *buffer = memget(len); 415 int idx = 0; 416 417 if (buffer == NULL) 418 return (NULL); 419 420 for (;;) { 421 if (irs_irp_read_line(pvt, line, sizeof line) <= 0 || 422 strchr(line, '\n') == NULL) 423 goto death; 424 425 linelen = strlen(line); 426 427 if (line[linelen - 1] != '\n') 428 goto death; 429 430 /* We're not strict about missing \r. Should we be?? */ 431 if (linelen > 2 && line[linelen - 2] == '\r') { 432 line[linelen - 2] = '\n'; 433 line[linelen - 1] = '\0'; 434 linelen--; 435 } 436 437 if (linelen == 2 && line[0] == '.') { 438 *size = len; 439 buffer[idx] = '\0'; 440 441 return (buffer); 442 } 443 444 if (linelen > (len - (idx + 1))) { 445 char *p = memget(len + LINEINCR); 446 447 if (p == NULL) 448 goto death; 449 memcpy(p, buffer, len); 450 memput(buffer, len); 451 buffer = p; 452 len += LINEINCR; 453 } 454 455 memcpy(buffer + idx, line, linelen); 456 idx += linelen; 457 } 458 death: 459 memput(buffer, len); 460 return (NULL); 461 } 462 463 /*% 464 * int irs_irp_get_full_response(struct irp_p *pvt, int *code, 465 * char **body, size_t *bodylen); 466 * 467 * Gets the response to a command. If the response indicates 468 * there's a body to follow(code % 10 == 1), then the 469 * body buffer is allcoated with memget and stored in 470 * *BODY. The length of the allocated body buffer is stored 471 * in *BODY. The caller must give the body buffer back to 472 * memput when done. The results code is stored in *CODE. 473 * 474 * Returns: 475 * 476 * 0 if a result was read. -1 on some sort of failure. 477 * 478 */ 479 480 int 481 irs_irp_get_full_response(struct irp_p *pvt, int *code, char *text, 482 size_t textlen, char **body, size_t *bodylen) { 483 int result = irs_irp_read_response(pvt, text, textlen); 484 485 *body = NULL; 486 487 if (result == 0) { 488 return (-1); 489 } 490 491 *code = result; 492 493 /* Code that matches 2xx is a good result code. 494 * Code that matches xx1 means there's a response body coming. 495 */ 496 if ((result / 100) == 2 && (result % 10) == 1) { 497 *body = irs_irp_read_body(pvt, bodylen); 498 if (*body == NULL) { 499 return (-1); 500 } 501 } 502 503 return (0); 504 } 505 506 /*% 507 * int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...); 508 * 509 * Sends command to remote connected via the PVT 510 * structure. FMT and args after it are fprintf-like 511 * arguments for formatting. 512 * 513 * Returns: 514 * 515 * 0 on success, -1 on failure. 516 */ 517 518 int 519 irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...) { 520 va_list ap; 521 char buffer[1024]; 522 int pos = 0; 523 int i, todo; 524 525 526 if (pvt->fdCxn < 0) { 527 return (-1); 528 } 529 530 va_start(ap, fmt); 531 (void) vsprintf(buffer, fmt, ap); 532 todo = strlen(buffer); 533 va_end(ap); 534 if (todo > (int)sizeof(buffer) - 3) { 535 syslog(LOG_CRIT, "memory overrun in irs_irp_send_command()"); 536 exit(1); 537 } 538 strcat(buffer, "\r\n"); 539 todo = strlen(buffer); 540 541 while (todo > 0) { 542 i = write(pvt->fdCxn, buffer + pos, todo); 543 #if 0 544 /* XXX brister */ 545 fprintf(stderr, "Wrote: \""); 546 fwrite(buffer + pos, sizeof (char), todo, stderr); 547 fprintf(stderr, "\"\n"); 548 #endif 549 if (i < 0) { 550 close(pvt->fdCxn); 551 pvt->fdCxn = -1; 552 return (-1); 553 } 554 todo -= i; 555 } 556 557 return (0); 558 } 559 560 561 /* Methods */ 562 563 /*% 564 * void irp_close(struct irs_acc *this) 565 * 566 */ 567 568 static void 569 irp_close(struct irs_acc *this) { 570 struct irp_p *irp = (struct irp_p *)this->private; 571 572 if (irp != NULL) { 573 irs_irp_disconnect(irp); 574 memput(irp, sizeof *irp); 575 } 576 577 memput(this, sizeof *this); 578 } 579 580 581 582 583 /*! \file */ 584