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