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