Home | History | Annotate | Line # | Download | only in irs
      1 /*	$NetBSD: irp.c,v 1.1.1.2 2012/09/09 16:07:50 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)
     21 static 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 
     55 static 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. */
     69 int 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  */
     84 struct irs_acc *
     85 irs_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 
    126 int
    127 irs_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  */
    152 int
    153 irs_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 
    258 int
    259 irs_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 
    270 void
    271 irs_irp_disconnect(struct irp_p *pvt) {
    272 	if (pvt->fdCxn != -1) {
    273 		close(pvt->fdCxn);
    274 		pvt->fdCxn = -1;
    275 	}
    276 }
    277 
    278 
    279 
    280 int
    281 irs_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 
    366 int
    367 irs_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 
    411 char *
    412 irs_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 
    482 int
    483 irs_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 
    520 int
    521 irs_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 
    570 static void
    571 irp_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