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