diff options
Diffstat (limited to 'contrib/bind/bin/irpd/irpd.c')
-rw-r--r-- | contrib/bind/bin/irpd/irpd.c | 2252 |
1 files changed, 2252 insertions, 0 deletions
diff --git a/contrib/bind/bin/irpd/irpd.c b/contrib/bind/bin/irpd/irpd.c new file mode 100644 index 0000000..a2b13cb --- /dev/null +++ b/contrib/bind/bin/irpd/irpd.c @@ -0,0 +1,2252 @@ +/* + * Copyright(c) 1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Notes. */ + +#if 0 + +I have to use an AF_INET. Ctl_server should probably take a AF arugment. + +The server has no way to issue any other greeting than HELLO. E.g., would +like to be able to drop connection on greeting if client is not comming +from 127.0.0.1. + +Need to fix client to handle response with body. + +should add iovec with body to the struct ctl_sess? + +should we close connections on some errors (like marshalling errors)? + +getnetbyname falls back to /etc/networks when named not running. Does not +seem to be so for getnetbyaddr + +#endif + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: irpd.c,v 1.7 1999/10/13 16:26:23 vixie Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* Imports. */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <assert.h> +#include <ctype.h> +#include <ctype.h> +#include <errno.h> +#include <grp.h> +#include <netdb.h> +#include <pwd.h> +#include <pwd.h> +#include <resolv.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <utmp.h> + +#ifdef EMPTY +/* Digital UNIX utmp.h defines this. */ +#undef EMPTY +#endif + +#include <isc/ctl.h> +#include <isc/assertions.h> +#include <isc/list.h> +#include <isc/memcluster.h> +#include <isc/logging.h> + +#include <irs.h> +#include <irp.h> +#include <isc/irpmarshall.h> +#include <irs_data.h> + +#include "port_after.h" + +/* Macros. */ + +#define ALLDIGITS(s) (strspn((s), "0123456789") == strlen((s))) + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +#define MAXNETNAMELEN 256 + +#if !defined(SUN_LEN) +#define SUN_LEN(su) \ + (sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path)) +#endif + +/* + * This macro is used to initialize a specified field of a net_data struct. + * If the initialization fails then an error response code is sent with a + * description of which field failed to be initialized. + * + * This is only meant for use at the start of the various verb functions. + */ + +#define ND_INIT(nd, field, sess, respcode) \ + do{ if ((nd)->field == 0) { \ + (nd)->field = (*(nd)->irs->field ## _map)(nd->irs); \ + if ((nd)->field == 0) { \ + char *msg = "net_data " #field " initialization failed"; \ + ctl_response(sess, respcode, msg, CTL_EXIT, NULL, \ + NULL, NULL, NULL, 0); \ + return; \ + } \ + } \ + } while (0) + +/* Data structures. */ + +struct arg_s { + struct iovec * iov; + int iovlen; +}; + +struct response_buff { + char * buff; + size_t bufflen; +}; + +struct client_ctx { + struct net_data * net_data; +}; + +/* Forwards. */ + +static struct response_buff *newbuffer(u_int length); +static void release_buffer(struct response_buff *b); +static struct arg_s *split_string(const char *string); +static void free_args(struct arg_s *args); +static int is_all_digits(char *p); +static struct client_ctx *make_cli_ctx(void); +static struct net_data *get_net_data(struct ctl_sess *sess); + +static void irpd_gethostbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_gethostbyname2(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_gethostbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_gethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_sethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getpwnam(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getpwuid(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_setpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getnetbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getnetbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_setnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getgrnam(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getgrgid(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_setgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getservbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getservbyport(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getservent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_setservent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getprotobyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getprotobynumber(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_setprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_getnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_innetgr(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_setnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_endnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_quit(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_help(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_accept(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); +static void irpd_abort(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx); + +static void irpd_done(struct ctl_sctx *ctx, struct ctl_sess *sess, + void *param); +static void response_done(struct ctl_sctx *ctx, struct ctl_sess *sess, + void *uap); +static void logger(enum ctl_severity, const char *fmt, ...); + +/* Constants. */ + +static const u_int hello_code = IRPD_WELCOME_CODE; +static const char hello_msg[] = "Welcome to IRPD (v 1)"; +static const u_int unkncode = 500; +static const u_int timeoutcode = 501; +static const u_int irpd_quit_ok = 201; +static const u_int timeout = IRPD_TIMEOUT; + +/* Globals. */ + +static int main_needs_exit = 0; +static evContext ev; + +struct ctl_verb verbs [] = { + { "gethostbyname", irpd_gethostbyname }, + { "gethostbyname2", irpd_gethostbyname2 }, + { "gethostbyaddr", irpd_gethostbyaddr }, + { "gethostent", irpd_gethostent }, + { "sethostent", irpd_sethostent }, +#ifdef WANT_IRS_PW + { "getpwnam", irpd_getpwnam }, + { "getpwuid", irpd_getpwuid }, + { "getpwent", irpd_getpwent }, + { "setpwent", irpd_setpwent }, +#endif + { "getnetbyname", irpd_getnetbyname }, + { "getnetbyaddr", irpd_getnetbyaddr }, + { "getnetent", irpd_getnetent }, + { "setnetent", irpd_setnetent }, +#ifdef WANT_IRS_GR + { "getgrnam", irpd_getgrnam }, + { "getgrgid", irpd_getgrgid }, + { "getgrent", irpd_getgrent }, + { "setgrent", irpd_setgrent }, +#endif + { "getservbyname", irpd_getservbyname }, + { "getservbyport", irpd_getservbyport }, + { "getservent", irpd_getservent }, + { "setservent", irpd_setservent }, + + { "getprotobyname", irpd_getprotobyname }, + { "getprotobynumber", irpd_getprotobynumber }, + { "getprotoent", irpd_getprotoent }, + { "setprotoent", irpd_setprotoent }, + + { "getnetgrent", irpd_getnetgrent }, + { "innetgr", irpd_innetgr }, + { "setnetgrent", irpd_setnetgrent }, + { "endnetgrent", irpd_endnetgrent }, + { "quit", irpd_quit }, + { "help", irpd_help }, + + { "", irpd_accept }, /* For connection setups. */ + + /* abort is a verb expected by the ctl library. Is called when the + * client drops the connection unexpectedly. + */ + { "abort", irpd_abort }, + + { NULL, NULL } +}; + +/* + * An empty string causes the library to use the compiled in + * defaults and to ignore any external files. + */ +char *conffile = ""; + +/* Public. */ + +int +main(int argc, char **argv) { + struct ctl_sctx *ctx; + struct sockaddr *addr; + struct sockaddr_un uaddr; + struct sockaddr_in iaddr; + log_channel chan; + short port = IRPD_PORT; + char *prog = argv[0]; + char *sockname = IRPD_PATH; + char *p; + int ch; + size_t socksize; + + addr = (struct sockaddr *)&iaddr; + socksize = sizeof iaddr; + + openlog("iprd", LOG_CONS|LOG_PID, LOG_DAEMON); + while ((ch = getopt(argc, argv, "u:p:c:")) != -1) { + switch(ch) { + case 'c': + conffile = optarg; + break; + + case 'p': + port = strtol(optarg, &p, 10); + if (*p != '\0') { + /* junk in argument */ + syslog(LOG_ERR, "port option not a number"); + exit(1); + } + break; + + case 'u': + sockname = optarg; + addr = (struct sockaddr *)&uaddr; + socksize = sizeof uaddr; + break; + + case 'h': + case '?': + default: + fprintf(stderr, "%s [ -c config-file ]\n", prog); + exit(1); + } + } + argc -= optind; + argv += optind; + + memset(&uaddr, 0, sizeof uaddr); + memset(&iaddr, 0, sizeof iaddr); + +#ifdef HAVE_SA_LEN + iaddr.sin_len = sizeof iaddr; +#endif + iaddr.sin_family = AF_INET; + iaddr.sin_port = htons(IRPD_PORT); + iaddr.sin_addr.s_addr = htonl(INADDR_ANY); + + uaddr.sun_family = AF_UNIX; + strncpy(uaddr.sun_path, sockname, sizeof uaddr.sun_path); +#ifdef HAVE_SA_LEN + uaddr.sun_len = SUN_LEN(&uaddr); +#endif + + if (addr == (struct sockaddr *)&uaddr) + socksize = SUN_LEN(&uaddr); + + /* XXX what if this file is not currently a socket? */ + unlink(sockname); + + evCreate(&ev); + + ctx = ctl_server(ev, addr, socksize, verbs, + unkncode, timeoutcode, /* IRPD_TIMEOUT */ 30, 5, + IRPD_MAXSESS, logger, NULL); + + INSIST(ctx != NULL); + + while (!main_needs_exit) { + evEvent event; + + INSIST_ERR(evGetNext(ev, &event, EV_WAIT) != -1); + INSIST_ERR(evDispatch(ev, event) != -1); + } + + return (0); +} + + +/* + * static void + * simple_response(struct ctl_sess *sess, u_int code, char *msg); + * Send back a simple, one-line response to the client. + */ +static void +simple_response(struct ctl_sess *sess, u_int code, char *msg) { + struct response_buff *b = newbuffer(strlen(msg) + 1); + + if (b == 0) + return; + strcpy(b->buff, msg); + ctl_response(sess, code, b->buff, 0, 0, response_done, b, NULL, 0); +} + +/* + * static void + * send_hostent(struct ctl_sess *sess, struct hostent *ho); + * Send a hostent struct over the wire. If HO is NULL, then + * a "No such host" is sent instead. + */ +static void +send_hostent(struct ctl_sess *sess, struct hostent *ho) { + if (ho == NULL) + simple_response(sess, IRPD_GETHOST_NONE, "No such host"); + else { + size_t need; + struct response_buff *b = newbuffer(0); + + if (irp_marshall_ho(ho, &b->buff, &b->bufflen) != 0) { + simple_response(sess, IRPD_GETHOST_ERROR, + "Internal error"); + logger(ctl_warning, + "Cannot marshall host data for %s\n", + ho->h_name); + release_buffer(b); + } else { + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETHOST_OK, "Host found", + 0, 0, response_done, + b, b->buff, strlen(b->buff)); + } + } +} + +/* + * static void + * do_gethostbyname2(struct ctl_sess *sess, struct net_data *nd, + * const char *hostname, int af); + * Look up the given HOSTNAME by Address-Family + * and then send the results to the client connected to + * SESS. + */ +static void +do_gethostbyname2(struct ctl_sess *sess, struct net_data *nd, + const char *hostname, int af) +{ + struct hostent *ho; + + ho = gethostbyname2_p(hostname, af, nd); + send_hostent(sess, ho); +} + +/* + * static void + * irpd_gethostbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETHOSTBYNAME verb. + */ +static void +irpd_gethostbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + char hname[MAXHOSTNAMELEN]; + struct arg_s *args; + int i; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETHOST_ERROR, + "Incorrect usage: GETHOSTBYNAME hostname"); + } else { + if (args->iov[0].iov_len >= sizeof hname) { + simple_response(sess, IRPD_GETHOST_ERROR, + "GETHOSTBYNAME: name too long"); + } else { + strncpy(hname, args->iov[0].iov_base, + args->iov[0].iov_len); + hname[args->iov[0].iov_len] = '\0'; + do_gethostbyname2(sess, netdata, hname, AF_INET); + } + } + free_args(args); +} + +/* + * static void + * irpd_gethostbyname2(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETHOSTBYNAME2 verb. + */ +static void +irpd_gethostbyname2(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + char hname[MAXHOSTNAMELEN]; + struct arg_s *args; + int i; + int af; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); + + args = split_string(rest); + if (args->iovlen != 3) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETHOST_ERROR, + "Incorrect usage: GETHOSTBYNAME2 hostname AF"); + } else if (args->iov[0].iov_len >= sizeof hname) { + simple_response(sess, IRPD_GETHOST_ERROR, + "GETHOSTBYNAME2: name too long"); + } else { + if (strncasecmp(args->iov[1].iov_base, "af_inet6", 8) == 0) + af = AF_INET6; + else if (strncasecmp(args->iov[1].iov_base, "af_inet", 7) == 0) + af = AF_INET; + else { + simple_response(sess, IRPD_GETHOST_ERROR, + "Unknown address family"); + goto untimely; + } + + strncpy(hname, args->iov[0].iov_base, + args->iov[0].iov_len); + hname[args->iov[0].iov_len] = '\0'; + do_gethostbyname2(sess, netdata, hname, af); + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_gethostbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETHOSTBYADDR verb. + */ +static void +irpd_gethostbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct hostent *ho; + char haddr[MAXHOSTNAMELEN]; + char tmpaddr[NS_IN6ADDRSZ]; + struct arg_s *args; + int i; + int af; + int addrlen; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); + + args = split_string(rest); + if (args->iovlen != 3) { + simple_response(sess, IRPD_GETHOST_ERROR, + "GETHOSTBYADDR addr afamily"); + } else { + if (args->iov[0].iov_len >= sizeof haddr) { + simple_response(sess, IRPD_GETHOST_ERROR, + "Address too long"); + } else { + strncpy(haddr, args->iov[1].iov_base, + args->iov[1].iov_len); + haddr[args->iov[1].iov_len] = '\0'; + if (strcasecmp(haddr, "af_inet") == 0) { + af = AF_INET; + addrlen = NS_INADDRSZ; + } else if (strcasecmp(haddr, "af_inet6") == 0) { + af = AF_INET6; + addrlen = NS_IN6ADDRSZ; + } else { + simple_response(sess, IRPD_GETHOST_ERROR, + "Unknown address family"); + goto untimely; + } + + strncpy(haddr, args->iov[0].iov_base, + args->iov[0].iov_len); + haddr[args->iov[0].iov_len] = '\0'; + + if (inet_pton(af, haddr, tmpaddr) != 1) { + simple_response(sess, IRPD_GETHOST_ERROR, + "Invalid address"); + goto untimely; + } + + ho = gethostbyaddr_p(tmpaddr, addrlen, af, netdata); + send_hostent(sess, ho); + } + } + + untimely: + free_args(args); +} + + +/* + * static void + * irpd_gethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETHOSTENT verb + */ +static void +irpd_gethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct hostent *ho; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); + + ho = gethostent_p(netdata); + + send_hostent(sess, ho); +} + +/* + * static void + * irpd_sethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the SETHOSTENT verb + */ +static void +irpd_sethostent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct hostent *ho; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ho, sess, IRPD_GETHOST_ERROR); + + sethostent_p(1, netdata); /* always stayopen */ + simple_response(sess, IRPD_GETHOST_SETOK, "ok"); +} + +#ifdef WANT_IRS_PW +/* + * static void + * send_pwent(struct ctl_sess *sess, struct passwd *pw); + * Send PW over the wire, or, if PW is NULL, a "No such + * user" response. + */ +static void +send_pwent(struct ctl_sess *sess, struct passwd *pw) { + if (pw == NULL) { + simple_response(sess, IRPD_GETUSER_NONE, + "No such user"); + } else { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_pw(pw, &b->buff, + &b->bufflen) != 0) { + simple_response(sess, IRPD_GETUSER_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall pw\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETUSER_OK, "User found", 0, 0, + response_done, b, b->buff, strlen(b->buff)); + } +} + +/* + * static void + * irpd_getpwnam(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETPWNAM verb + */ +static void +irpd_getpwnam(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct passwd *pw; + char username[64]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETUSER_ERROR, + "GETPWNAM username"); + } else { + if (args->iov[0].iov_len >= sizeof username) { + simple_response(sess, IRPD_GETUSER_ERROR, + "Name too long"); + } else { + strncpy(username, args->iov[0].iov_base, + args->iov[0].iov_len); + username[args->iov[0].iov_len] = '\0'; + + pw = getpwnam_p(username, netdata); + send_pwent(sess, pw); + } + } + + free_args(args); +} + +/* + * static void + * irpd_getpwuid(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETPWUID verb. + */ +static void +irpd_getpwuid(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct passwd *pw; + char userid[64]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETUSER_ERROR, + "GETPWUID uid"); + } else { + if (args->iov[0].iov_len >= sizeof userid) { + simple_response(sess, IRPD_GETUSER_ERROR, + "Name too long"); + } else { + strncpy(userid, args->iov[0].iov_base, + args->iov[0].iov_len); + userid[args->iov[0].iov_len] = '\0'; + + if (!ALLDIGITS(userid)) { + simple_response(sess, IRPD_GETUSER_ERROR, + "Not a uid"); + } else { + uid_t uid; + long lval; + + lval = strtol(userid, 0, 10); + uid = (uid_t)lval; + if ((long)uid != lval) { + /* value was too big */ + simple_response(sess, + IRPD_GETUSER_ERROR, + "Not a valid uid"); + goto untimely; + } + + pw = getpwuid_p(uid, netdata); + send_pwent(sess, pw); + } + } + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_getpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implemtnation of the GETPWENT verb. + */ +static void +irpd_getpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct passwd *pw; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); + + pw = getpwent_p(netdata); + send_pwent(sess, pw); +} + +/* + * static void + * irpd_setpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implemtnation of the SETPWENT verb. + */ +static void +irpd_setpwent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct passwd *pw; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pw, sess, IRPD_GETUSER_ERROR); + + setpwent_p(netdata); + simple_response(sess, IRPD_GETUSER_SETOK, "ok"); +} +#endif /* WANT_IRS_PW */ + +/* + * static void + * send_nwent(struct ctl_sess *sess, struct nwent *ne); + * Sends a nwent structure over the wire, or "No such + * network" if NE is NULL. + */ +static void +send_nwent(struct ctl_sess *sess, struct nwent *nw) { + if (nw == NULL) { + simple_response(sess, IRPD_GETNET_NONE, "No such net"); + } else { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_nw(nw, &b->buff, + &b->bufflen) != 0) { + simple_response(sess, IRPD_GETNET_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall nw\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETNET_OK, "Network found", 0, 0, + response_done, b, b->buff, strlen(b->buff)); + } +} + +/* + * static void + * send_netent(struct ctl_sess *sess, struct netent *ne); + * Sends a NETENT structure over the wire, or "No such + * Network" error if NE is NULL. + */ +static void +send_netent(struct ctl_sess *sess, struct netent *ne) { + if (ne == NULL) { + simple_response(sess, IRPD_GETNET_NONE, "No such net"); + } else { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_ne(ne, &b->buff, + &b->bufflen) != 0) { + simple_response(sess, IRPD_GETNET_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall ne\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETNET_OK, "Network found", 0, 0, + response_done, b, b->buff, strlen(b->buff)); + } +} + +/* + * static void + * irpd_getnetbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of GETNETBYNAME verb. + */ +static void +irpd_getnetbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct netent *ne; + struct nwent *nw; + char netname[MAXNETNAMELEN]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, nw, sess, IRPD_GETNET_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETNET_ERROR, + "GETNETBYNAME name"); + } else { + if (args->iov[0].iov_len >= sizeof netname) { + simple_response(sess, IRPD_GETNET_ERROR, + "Name too long"); + } else { + strncpy(netname, args->iov[0].iov_base, + args->iov[0].iov_len); + netname[args->iov[0].iov_len] = '\0'; + + ne = getnetbyname_p(netname, netdata); + + /* The public interface only gives us a struct + netent, and we need a struct nwent that irs uses + internally, so we go dig it out ourselves. Yuk + */ + nw = NULL; + if (ne != NULL) { + /* Puke. */ + INSIST(netdata->nw_last == ne); + nw = netdata->nww_last; + } + + send_nwent(sess, nw); + } + } + free_args(args); +} + +/* + * static void + * irpd_getnetbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + */ +static void +irpd_getnetbyaddr(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct netent *ne; + struct nwent *nw; + char haddr[MAXHOSTNAMELEN]; + long tmpaddr; + struct arg_s *args; + int i; + int af; + int addrlen; + int bits; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, nw, sess, IRPD_GETUSER_ERROR); + + args = split_string(rest); + if (args->iovlen != 3) { + simple_response(sess, IRPD_GETNET_ERROR, + "GETNETBYADDR addr afamily"); + } else { + if (args->iov[0].iov_len >= sizeof haddr) { + simple_response(sess, IRPD_GETNET_ERROR, + "Address too long"); + } else { + strncpy(haddr, args->iov[1].iov_base, + args->iov[1].iov_len); + haddr[args->iov[1].iov_len] = '\0'; + if (strcasecmp(haddr, "af_inet") == 0) { + af = AF_INET; + addrlen = NS_INADDRSZ; + } else if (strcasecmp(haddr, "af_inet6") == 0) { + af = AF_INET6; + addrlen = NS_IN6ADDRSZ; + + /* XXX the interface we use(getnetbyaddr) + * can't handle AF_INET6, so for now we + * bail. + */ + simple_response(sess, IRPD_GETNET_ERROR, + "AF_INET6 unsupported"); + goto untimely; + } else { + simple_response(sess, IRPD_GETNET_ERROR, + "Unknown address family"); + goto untimely; + } + + strncpy(haddr, args->iov[0].iov_base, + args->iov[0].iov_len); + haddr[args->iov[0].iov_len] = '\0'; + + bits = inet_net_pton(af, haddr, + &tmpaddr, sizeof tmpaddr); + if (bits < 0) { + simple_response(sess, IRPD_GETNET_ERROR, + "Invalid address"); + goto untimely; + } + + ne = getnetbyaddr_p(tmpaddr, af, netdata); + + /* The public interface only gives us a struct + netent, and we need a struct nwent that irs uses + internally, so we go dig it out ourselves. Yuk + */ + nw = NULL; + if (ne != NULL) { + /* Puke puke */ + INSIST(netdata->nw_last == ne); + nw = netdata->nww_last; + } + + send_nwent(sess, nw); + } + } + + untimely: + free_args(args); +} + + +/* + * static void + * irpd_getnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETNETENT verb. + */ +static void +irpd_getnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct netent *ne; + struct nwent *nw; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, nw, sess, IRPD_GETNET_ERROR); + + ne = getnetent_p(netdata); + nw = NULL; + if (ne != NULL) { + /* triple puke */ + INSIST(netdata->nw_last == ne); + nw = netdata->nww_last; + } + send_nwent(sess, nw); +} + +/* + * static void + * irpd_setnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the SETNETENT verb. + */ +static void +irpd_setnetent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct netent *ne; + struct nwent *nw; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, nw, sess, IRPD_GETNET_ERROR); + + setnetent_p(1, netdata); /* always stayopen */ + simple_response(sess, IRPD_GETNET_SETOK, "ok"); +} + +#ifdef WANT_IRS_GR +/* + * static void + * send_grent(struct ctl_sess *sess, struct group *gr); + * Marshall GR and send as body of response. If GR is NULL + * then a "No such group" response is sent instead. + */ +static void +send_grent(struct ctl_sess *sess, struct group *gr) { + if (gr == NULL) { + simple_response(sess, IRPD_GETGROUP_NONE, + "No such user"); + } else { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_gr(gr, &b->buff, &b->bufflen) != 0) { + simple_response(sess, IRPD_GETGROUP_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall gr\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETGROUP_OK, "Group found", 0, 0, + response_done, b, b->buff, strlen(b->buff)); + } +} + +/* + * static void + * irpd_getgrnam(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETGRNAM verb. + */ +static void +irpd_getgrnam(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct group *gr; + char groupname[64]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETGROUP_ERROR, + "GETGRNAM groupname"); + } else { + if (args->iov[0].iov_len >= sizeof groupname) { + simple_response(sess, IRPD_GETGROUP_ERROR, + "Name too long"); + } else { + strncpy(groupname, args->iov[0].iov_base, + args->iov[0].iov_len); + groupname[args->iov[0].iov_len] = '\0'; + + gr = getgrnam_p(groupname, netdata); + send_grent(sess, gr); + } + } + + free_args(args); +} + +/* + * static void + * irpd_getgrgid(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implentation of the GETGRGID verb. + */ +static void +irpd_getgrgid(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct group *gr; + char groupid[64]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETGROUP_ERROR, + "GETGRUID gid"); + } else { + if (args->iov[0].iov_len >= sizeof groupid) { + simple_response(sess, IRPD_GETGROUP_ERROR, + "Name too long"); + } else { + strncpy(groupid, args->iov[0].iov_base, + args->iov[0].iov_len); + groupid[args->iov[0].iov_len] = '\0'; + + if (!ALLDIGITS(groupid)) { + simple_response(sess, IRPD_GETGROUP_ERROR, + "Not a gid"); + } else { + gid_t gid; + long lval; + + lval = strtol(groupid, 0, 10); + gid = (gid_t)lval; + if ((long)gid != lval) { + /* value was too big */ + simple_response(sess, + IRPD_GETGROUP_ERROR, + "Not a valid gid"); + goto untimely; + } + + gr = getgrgid_p(gid, netdata); + send_grent(sess, gr); + } + } + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_getgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the GETGRENT verb. + */ +static void +irpd_getgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct group *gr; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); + + gr = getgrent_p(netdata); + send_grent(sess, gr); +} + +/* + * static void + * irpd_setgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Implementation of the SETGRENT verb. + */ +static void +irpd_setgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct group *gr; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, gr, sess, IRPD_GETGROUP_ERROR); + + setgrent_p(netdata); + simple_response(sess, IRPD_GETGROUP_SETOK, "ok"); +} +#endif /* WANT_IRS_GR */ + +static void +send_servent(struct ctl_sess *sess, struct servent *serv) { + if (serv == NULL) { + simple_response(sess, IRPD_GETSERVICE_NONE, + "No such service"); + } else { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_sv(serv, &b->buff, + &b->bufflen) != 0) { + simple_response(sess, IRPD_GETSERVICE_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall servent\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETSERVICE_OK, "Service found", 0, 0, + response_done, b, b->buff, strlen(b->buff)); + } +} + +static void +irpd_getservbyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct servent *serv; + char servicename[64]; + char protoname[10]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); + + args = split_string(rest); + if (args->iovlen != 3) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETSERVICE_ERROR, + "GETSERVNAM servicename protocol"); + } else { + if (args->iov[0].iov_len >= sizeof servicename) { + simple_response(sess, IRPD_GETSERVICE_ERROR, + "Invalid service name"); + } else if (args->iov[1].iov_len >= sizeof protoname) { + simple_response(sess, IRPD_GETSERVICE_ERROR, + "Invalid protocol name"); + } else { + strncpy(servicename, args->iov[0].iov_base, + args->iov[0].iov_len); + servicename[args->iov[0].iov_len] = '\0'; + + strncpy(protoname, args->iov[1].iov_base, + args->iov[1].iov_len); + protoname[args->iov[1].iov_len] = '\0'; + + serv = getservbyname_p(servicename, protoname, + netdata); + send_servent(sess, serv); + } + } + + free_args(args); +} + +/* + * static void + * irpd_getservbyport(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the GETSERVBYPORT verb. + */ +static void +irpd_getservbyport(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct servent *sv; + char portnum[64]; + char protoname[10]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); + + args = split_string(rest); + if (args->iovlen != 3) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETSERVICE_ERROR, + "GETSERVBYPORT port protocol"); + } else { + if (args->iov[0].iov_len >= sizeof portnum) { + simple_response(sess, IRPD_GETSERVICE_ERROR, + "Invalid port"); + } else if (args->iov[1].iov_len > sizeof protoname - 1) { + simple_response(sess, IRPD_GETSERVICE_ERROR, + "Invalid protocol"); + } else { + strncpy(portnum, args->iov[0].iov_base, + args->iov[0].iov_len); + portnum[args->iov[0].iov_len] = '\0'; + + strncpy(protoname, args->iov[1].iov_base, + args->iov[1].iov_len); + protoname[args->iov[1].iov_len] = '\0'; + + if (!ALLDIGITS(portnum)) { + simple_response(sess, IRPD_GETSERVICE_ERROR, + "Not a port number"); + } else { + short port; + long lval; + + lval = strtol(portnum, 0, 10); + port = (short)lval; + if ((long)port != lval) { + /* value was too big */ + simple_response(sess, + IRPD_GETSERVICE_ERROR, + "Not a valid port"); + goto untimely; + } + port = htons(port); + + sv = getservbyport_p(port, protoname, netdata); + send_servent(sess, sv); + } + } + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_getservent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the GETSERVENT verb. + */ +static void +irpd_getservent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct servent *sv; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); + + sv = getservent_p(netdata); + send_servent(sess, sv); +} + +/* + * static void + * irpd_setservent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the SETSERVENT verb. + */ +static void +irpd_setservent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct servent *sv; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, sv, sess, IRPD_GETSERVICE_ERROR); + + setservent_p(1, netdata); /* always stay open */ + simple_response(sess, IRPD_GETSERVICE_SETOK, "ok"); +} + +/* + * static void + * send_prent(struct ctl_sess *sess, struct protoent *pr); + * Send the PR structure over the wire. If PR is NULL, then + * the response "No such protocol" is sent instead. + */ +static void +send_prent(struct ctl_sess *sess, struct protoent *pr) { + if (pr == NULL) { + simple_response(sess, IRPD_GETPROTO_NONE, + "No such protocol"); + } else { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_pr(pr, &b->buff, + &b->bufflen) != 0) { + simple_response(sess, IRPD_GETPROTO_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall pr\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETPROTO_OK, "Protocol found", 0, 0, + response_done, b, b->buff, strlen(b->buff)); + } +} + +/* + * static void + * irpd_getprotobyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the GETPROTOBYNAME verb. + */ +static void +irpd_getprotobyname(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct protoent *pr; + char protoname[64]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETPROTO_ERROR, + "GETPROTOBYNAME protocol"); + } else { + if (args->iov[0].iov_len >= sizeof protoname) { + simple_response(sess, IRPD_GETPROTO_ERROR, + "Name too long"); + } else { + strncpy(protoname, args->iov[0].iov_base, + args->iov[0].iov_len); + protoname[args->iov[0].iov_len] = '\0'; + + pr = getprotobyname_p(protoname, netdata); + send_prent(sess, pr); + } + } + free_args(args); +} + +/* + * static void + * irpd_getprotobynumber(struct ctl_sctx *ctx, + * struct ctl_sess *sess, const struct ctl_verb *verb, + * const char *rest, u_int respflags, void *respctx, + * void *uctx); + * Handle the GETPROTOBYNUMBER verb. + */ +static void +irpd_getprotobynumber(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct protoent *pr; + char protonum[64]; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETPROTO_ERROR, + "GETPROTOBYNUMBER protocol"); + } else { + if (args->iov[0].iov_len >= sizeof protonum) { + simple_response(sess, IRPD_GETGROUP_ERROR, + "Name too long"); + } else { + strncpy(protonum, args->iov[0].iov_base, + args->iov[0].iov_len); + protonum[args->iov[0].iov_len] = '\0'; + + if (!ALLDIGITS(protonum)) { + simple_response(sess, IRPD_GETPROTO_ERROR, + "Not a protocol number"); + } else { + int proto; + long lval; + + lval = strtol(protonum, 0, 10); + proto = (int)lval; + if ((long)proto != lval) { + /* value was too big */ + simple_response(sess, + IRPD_GETPROTO_ERROR, + "Not a valid proto"); + goto untimely; + } + + pr = getprotobynumber_p(proto, netdata); + send_prent(sess, pr); + } + } + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_getprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the GETPROTOENT verb. + */ +static void +irpd_getprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct protoent *pr; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); + + pr = getprotoent_p(netdata); + send_prent(sess, pr); +} + +/* + * static void + * irpd_setprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the SETPROTOENT verb. + */ +static void +irpd_setprotoent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct protoent *pr; + size_t need; + size_t need_total = 0; + struct response_buff *b; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, pr, sess, IRPD_GETPROTO_ERROR); + + setprotoent_p(1, netdata); /* always stay open */ + simple_response(sess, IRPD_GETPROTO_SETOK, "ok"); +} + +/* + * static void + * send_pwent(struct ctl_sess *sess, struct passwd *pw); + * Send PW over the wire, or, if PW is NULL, a "No such + * user" response. + */ +static void +send_ngent(struct ctl_sess *sess, char *host, char *user, char *domain) { + struct response_buff *b = newbuffer(0); + + if (irp_marshall_ng(host, user, domain, &b->buff, + &b->bufflen) != 0) { + simple_response(sess, IRPD_GETNETGR_ERROR, + "Internal error"); + logger(ctl_warning, "Cant marshall ng\n"); + return; + } + + strcat(b->buff, "\r\n"); + + ctl_response(sess, IRPD_GETNETGR_OK, "Netgroup entry", 0, 0, + response_done, b, b->buff, strlen(b->buff)); +} + +/* + * static void + * irpd_getnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the GETNETGRENT verb. + */ +static void +irpd_getnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + char netgroupname[64]; + struct response_buff *b = NULL; + size_t need; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); + + if (rest != NULL && strlen(rest) > 0) { + simple_response(sess, IRPD_GETNETGR_ERROR, + "GETNETGRENT"); + } else { + char *host, *user, *domain; + + if (getnetgrent_p(&host, &user, &domain, netdata) == 1) { + send_ngent(sess, host, user, domain); + } else { + simple_response(sess, IRPD_GETNETGR_NOMORE, + "No more"); + } + } +} + +/* + * static void + * irpd_innetgr(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the INNETGR verb. + */ +static void +irpd_innetgr(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct response_buff *b; + size_t need; + struct net_data *netdata = get_net_data(sess); + char *host; + char *user; + char *domain; + + INSIST(netdata != NULL); + + ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); + + args = split_string(rest); + if (args->iovlen != 3) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETNETGR_ERROR, + "INNETGR netgroup ngentry"); + } else { + char *grptmp = memget(args->iov[0].iov_len + 1); + char *ngtmp = memget(args->iov[1].iov_len + 1); + + strncpy(grptmp, args->iov[0].iov_base, args->iov[0].iov_len); + strncpy(ngtmp, args->iov[1].iov_base, args->iov[1].iov_len); + + grptmp[args->iov[0].iov_len] = '\0'; + ngtmp[args->iov[1].iov_len] = '\0'; + + if (irp_unmarshall_ng(&host, &user, &domain, ngtmp) != 0) { + simple_response(sess, IRPD_GETNETGR_ERROR, + "ngentry must be (host,user,domain)"); + } else { + if (innetgr_p(grptmp, host, user, domain, + netdata) == 1) { + simple_response(sess, IRPD_GETNETGR_MATCHES, + "INNETGR matches"); + } else { + simple_response(sess, IRPD_GETNETGR_NOMATCH, + "INNETGR does not match"); + } + } + + memput(grptmp, args->iov[0].iov_len + 1); + memput(ngtmp, args->iov[1].iov_len + 1); + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_setnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the SETNETGRENT verb. + */ +static void +irpd_setnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); + + args = split_string(rest); + if (args->iovlen != 2) { /* len includes NULL at end */ + simple_response(sess, IRPD_GETNETGR_ERROR, + "setnetgrent netgroup"); + } else { + setnetgrent_p(rest, netdata); + simple_response(sess, IRPD_GETNETGR_SETOK, + "setnetgrent ok"); + } + + untimely: + free_args(args); +} + +/* + * static void + * irpd_endnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the ENDNETGRENT verb. + */ +static void +irpd_endnetgrent(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct arg_s *args; + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + ND_INIT(netdata, ng, sess, IRPD_GETNETGR_ERROR); + + if (rest != NULL && strlen (rest) > 0) { + simple_response(sess, IRPD_GETNETGR_ERROR, + "endnetgrent netgroup"); + } else { + endnetgrent_p(netdata); + simple_response(sess, IRPD_GETNETGR_SETOK, + "endnetgrent ok"); + } +} + +/* + * static void + * irpd_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *param) + * Callback for when QUIT respnse is sent out. + */ +static void +irpd_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *param) { + struct net_data *netdata = get_net_data(sess); + + INSIST(netdata != NULL); + + net_data_destroy(netdata); +} + +/* + * static void + * irpd_quit(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the QUIT verb. + */ +static void +irpd_quit(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + ctl_response(sess, irpd_quit_ok, "See ya!", CTL_EXIT, NULL, + 0 , NULL, NULL, 0); +} + +/* + * static void + * irpd_help(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle the HELP verb. + */ +static void +irpd_help(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + /* XXX should make this do something better (like include required + * arguments. + */ + ctl_sendhelp(sess, 231); +} + +/* + * static void + * irpd_accept(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle a new connection. + */ +static void +irpd_accept(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct sockaddr *sa = respctx; + char raddr[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + int reject = 1; + int response; + char *respmsg = NULL; + + if (sa->sa_family == AF_UNIX) { + syslog (LOG_INFO, "New AF_UNIX connection"); + reject = 0; + } else if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = respctx; + static long localhost; + static long zero; + + if (localhost == 0) { + /* yes, this could be done with simple arithmetic... */ + inet_pton(AF_INET, "127.0.0.1", &localhost); + } + + inet_ntop(AF_INET, &sin->sin_addr, raddr, sizeof raddr); + + /* we reject INET connections that are not from the local + * machine. + */ + if (sin->sin_addr.s_addr == zero || + sin->sin_addr.s_addr == localhost) { + reject = 0; + syslog(LOG_INFO, "New connection from %s", raddr); + } else { + syslog(LOG_INFO, "New connection from %s (reject)", + raddr); + respmsg = "Connections from off host not permitted"; + } + } else if (sa->sa_family == AF_INET6) { + /* XXX should do something intelligent here. */ + respmsg = "IPv6 connections not implemented yet."; + syslog(LOG_ERR, "Cannot handle AF_INET6 connections yet"); + } else { + syslog (LOG_ERR, "Unknown peer type: %d", sa->sa_family); + respmsg = "What are you???"; + } + + if (reject) { + response = IRPD_NOT_WELCOME_CODE; + if (respmsg == NULL) { + respmsg = "Go away!"; + } + /* XXX can we be sure that stacked up commands will not be + * processed before the control connection is closed??? + */ + } else { + void *ctx = make_cli_ctx(); + + if (ctx == NULL) { + response = IRPD_NOT_WELCOME_CODE; + respmsg = "Internal error (client context)"; + } else { + response = IRPD_WELCOME_CODE; + if (respmsg == NULL) { + respmsg = "Welcome to IRPD (v 1)"; + } + ctl_setcsctx(sess, ctx); + } + } + ctl_response(sess, response, respmsg, (reject ? CTL_EXIT : 0), NULL, + 0, NULL, NULL, 0); +} + +/* + * static void + * irpd_abort(struct ctl_sctx *ctx, struct ctl_sess *sess, + * const struct ctl_verb *verb, const char *rest, + * u_int respflags, void *respctx, void *uctx); + * Handle a dropped connection. + */ +static void +irpd_abort(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *rest, + u_int respflags, void *respctx, void *uctx) +{ + struct net_data *netdata = get_net_data(sess); + + if (netdata != NULL) + net_data_destroy(netdata); +} + +/* + * void + * response_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *uap) + * UAP is the response_buffer passed through to + * ctl_response. + */ +static void +response_done(struct ctl_sctx *ctx, struct ctl_sess *sess, void *uap) { + release_buffer(uap); +} + +/* + * static void + * logger(enum ctl_severity sev, const char *fmt, ...); + * Logging routine called by the ctl_* functions. For now we + * just spit everything to stderr. + */ + +static void +logger(enum ctl_severity sev, const char *fmt, ...) { + char buffer[1024]; + va_list ap; + int level; + + if (sev == ctl_debug) + return; + + if (sev == ctl_warning) + level = LOG_WARNING; + else if (sev == ctl_error) + level = LOG_ERR; + else { + syslog(LOG_CRIT, "Invalid severity: %d", (int)sev); + exit(1); + } + + va_start(ap, fmt); + +#if 0 + fprintf(stderr, "irpd: "); + vfprintf(stderr, fmt, ap); +#else + if (vsprintf(buffer, fmt, ap) > (sizeof (buffer) - 1)) { + syslog(LOG_CRIT, "Buffer overrun in logger"); + abort(); + } + syslog(level, "%s", buffer); +#endif + va_end(ap); +} + +/* + * static struct response_buff * + * newbuffer(u_int length); + * Create a structure to hold an allocated buffer. We do + * this so we can get the size to deallocate later. + * Returns: + * Pointer to the structure + */ +static struct response_buff * +newbuffer(u_int length) { + struct response_buff *h; + + h = memget(sizeof *h); + if (h == NULL) { + errno = ENOMEM; + return (NULL); + } + + h->buff = NULL; + h->bufflen = length; + + if (length > 0) { + h->buff = memget(h->bufflen); + if (h->buff == NULL) { + memput(h, sizeof *h); + errno = ENOMEM; + return (NULL); + } + memset(h->buff, 0, h->bufflen); + } + + return (h); +} + +/* + * static void + * release_buffer(struct response_buff *b); + * Free up a buffer allocated with newbuffer. + */ +static void +release_buffer(struct response_buff *b) { + memset(b->buff, 0, b->bufflen); + memput(b->buff, b->bufflen); + + memset(b, 0, sizeof *b); + memput(b, sizeof *b); +} + +/* + * static struct arg_s * + * split_string(const char *string); + * Create an array of iovecs(last one having NULL fields) + * pointing into STRING at the non-whitespace sections. The + * iovecs are stashed inside a structure so we can get the + * size back later at deallocation time. Iovecs are used to avoid + * modifying the argument with added nulls. + * Returns: + * Pointer to the wrapper structure. Must be given to free_args() + * when done + */ +static struct arg_s * +split_string(const char *string) { + struct iovec *iovs; + const char *p; + int i, c, iswh; + struct arg_s *a; + + /* count + 1 of the number of runs of non-whitespace. */ + for (iswh = 1, i = 1, p = string ; p != NULL && *p ; p++) { + if (iswh && !isspace(*p)) { + iswh = 0; + i++; + } else if (!iswh && isspace(*p)) { + iswh = 1; + } + } + + iovs = memget(sizeof (struct iovec) * i); + if (iovs == NULL) { + errno = ENOMEM; + return (NULL); + } + + a = memget(sizeof *a); + if (a == NULL) { + errno = ENOMEM; + memput(iovs, sizeof (struct iovec) * i); + return (NULL); + } + a->iov = iovs; + a->iovlen = i; + + for (c = 0, p = string ; p != NULL && *p ; c++) { + while (isspace(*p)) { + p++; + } + + if (*p == '\0') + break; + + iovs[c].iov_base = (void *)p; + + while (*p && !isspace(*p)) { + p++; + } + iovs[c].iov_len = p - (char *)iovs[c].iov_base; + } + INSIST(c == i - 1); + iovs[c].iov_base = NULL; + iovs[c].iov_len = 0; + + return (a); +} + +/* + * static void + * free_args(struct arg_s *args); + * Free up the argument structure created with + * split_string(). + */ + +static void +free_args(struct arg_s *args) { + memput(args->iov, sizeof (struct iovec) * args->iovlen); + memput(args, sizeof *args); +} + +static struct client_ctx * +make_cli_ctx(void) { + struct client_ctx *p = memget (sizeof *p); + + if (p == NULL) + return (NULL); + + p->net_data = net_data_create(conffile); + + return (p); +} + +static void +release_cli_ctx(struct client_ctx *ctx) { + INSIST(ctx != NULL); + INSIST(ctx->net_data != NULL); + + net_data_destroy(ctx->net_data); + memput(ctx, sizeof *ctx); +} + +static struct net_data * +get_net_data(struct ctl_sess *sess) { + struct client_ctx *ctx = ctl_getcsctx(sess); + + INSIST(ctx != NULL); + INSIST(ctx->net_data != NULL); + + return (ctx->net_data); +} |