diff options
Diffstat (limited to 'bin/auditdistd/proto.c')
-rw-r--r-- | bin/auditdistd/proto.c | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/bin/auditdistd/proto.c b/bin/auditdistd/proto.c new file mode 100644 index 000000000000..25daf0ccb0db --- /dev/null +++ b/bin/auditdistd/proto.c @@ -0,0 +1,529 @@ +/*- + * Copyright (c) 2009-2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/proto.c#1 $ + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> + +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <strings.h> + +#include "pjdlog.h" +#include "proto.h" +#include "proto_impl.h" + +#define PROTO_CONN_MAGIC 0x907041c +struct proto_conn { + int pc_magic; + struct proto *pc_proto; + void *pc_ctx; + int pc_side; +#define PROTO_SIDE_CLIENT 0 +#define PROTO_SIDE_SERVER_LISTEN 1 +#define PROTO_SIDE_SERVER_WORK 2 +}; + +static TAILQ_HEAD(, proto) protos = TAILQ_HEAD_INITIALIZER(protos); + +void +proto_register(struct proto *proto, bool isdefault) +{ + static bool seen_default = false; + + if (!isdefault) + TAILQ_INSERT_HEAD(&protos, proto, prt_next); + else { + PJDLOG_ASSERT(!seen_default); + seen_default = true; + TAILQ_INSERT_TAIL(&protos, proto, prt_next); + } +} + +static struct proto_conn * +proto_alloc(struct proto *proto, int side) +{ + struct proto_conn *conn; + + PJDLOG_ASSERT(proto != NULL); + PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT || + side == PROTO_SIDE_SERVER_LISTEN || + side == PROTO_SIDE_SERVER_WORK); + + conn = malloc(sizeof(*conn)); + if (conn != NULL) { + conn->pc_proto = proto; + conn->pc_side = side; + conn->pc_magic = PROTO_CONN_MAGIC; + } + return (conn); +} + +static void +proto_free(struct proto_conn *conn) +{ + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT || + conn->pc_side == PROTO_SIDE_SERVER_LISTEN || + conn->pc_side == PROTO_SIDE_SERVER_WORK); + PJDLOG_ASSERT(conn->pc_proto != NULL); + + bzero(conn, sizeof(*conn)); + free(conn); +} + +static int +proto_common_setup(const char *srcaddr, const char *dstaddr, int timeout, + int side, struct proto_conn **connp) +{ + struct proto *proto; + struct proto_conn *conn; + void *ctx; + int ret; + + PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT || + side == PROTO_SIDE_SERVER_LISTEN); + + TAILQ_FOREACH(proto, &protos, prt_next) { + if (side == PROTO_SIDE_CLIENT) { + if (proto->prt_connect == NULL) { + ret = -1; + } else { + ret = proto->prt_connect(srcaddr, dstaddr, + timeout, &ctx); + } + } else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ { + if (proto->prt_server == NULL) + ret = -1; + else + ret = proto->prt_server(dstaddr, &ctx); + } + /* + * ret == 0 - success + * ret == -1 - dstaddr is not for this protocol + * ret > 0 - right protocol, but an error occured + */ + if (ret >= 0) + break; + } + if (proto == NULL) { + /* Unrecognized address. */ + errno = EINVAL; + return (-1); + } + if (ret > 0) { + /* An error occured. */ + errno = ret; + return (-1); + } + conn = proto_alloc(proto, side); + if (conn == NULL) { + if (proto->prt_close != NULL) + proto->prt_close(ctx); + errno = ENOMEM; + return (-1); + } + conn->pc_ctx = ctx; + *connp = conn; + + return (0); +} + +int +proto_connect(const char *srcaddr, const char *dstaddr, int timeout, + struct proto_conn **connp) +{ + + PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0'); + PJDLOG_ASSERT(dstaddr != NULL); + PJDLOG_ASSERT(timeout >= -1); + + return (proto_common_setup(srcaddr, dstaddr, timeout, + PROTO_SIDE_CLIENT, connp)); +} + +int +proto_connect_wait(struct proto_conn *conn, int timeout) +{ + int error; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_connect_wait != NULL); + PJDLOG_ASSERT(timeout >= 0); + + error = conn->pc_proto->prt_connect_wait(conn->pc_ctx, timeout); + if (error != 0) { + errno = error; + return (-1); + } + + return (0); +} + +int +proto_server(const char *addr, struct proto_conn **connp) +{ + + PJDLOG_ASSERT(addr != NULL); + + return (proto_common_setup(NULL, addr, -1, PROTO_SIDE_SERVER_LISTEN, + connp)); +} + +int +proto_accept(struct proto_conn *conn, struct proto_conn **newconnp) +{ + struct proto_conn *newconn; + int error; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_accept != NULL); + + newconn = proto_alloc(conn->pc_proto, PROTO_SIDE_SERVER_WORK); + if (newconn == NULL) + return (-1); + + error = conn->pc_proto->prt_accept(conn->pc_ctx, &newconn->pc_ctx); + if (error != 0) { + proto_free(newconn); + errno = error; + return (-1); + } + + *newconnp = newconn; + + return (0); +} + +int +proto_send(const struct proto_conn *conn, const void *data, size_t size) +{ + int error; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL); + + error = conn->pc_proto->prt_send(conn->pc_ctx, data, size, -1); + if (error != 0) { + errno = error; + return (-1); + } + return (0); +} + +int +proto_recv(const struct proto_conn *conn, void *data, size_t size) +{ + int error; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL); + + error = conn->pc_proto->prt_recv(conn->pc_ctx, data, size, NULL); + if (error != 0) { + errno = error; + return (-1); + } + return (0); +} + +int +proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn) +{ + const char *protoname; + int error, fd; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL); + PJDLOG_ASSERT(mconn != NULL); + PJDLOG_ASSERT(mconn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(mconn->pc_proto != NULL); + fd = proto_descriptor(mconn); + PJDLOG_ASSERT(fd >= 0); + protoname = mconn->pc_proto->prt_name; + PJDLOG_ASSERT(protoname != NULL); + + error = conn->pc_proto->prt_send(conn->pc_ctx, + (const unsigned char *)protoname, strlen(protoname) + 1, fd); + proto_close(mconn); + if (error != 0) { + errno = error; + return (-1); + } + return (0); +} + +int +proto_wrap(const char *protoname, bool client, int fd, + struct proto_conn **newconnp) +{ + struct proto *proto; + struct proto_conn *newconn; + int error; + + TAILQ_FOREACH(proto, &protos, prt_next) { + if (strcmp(proto->prt_name, protoname) == 0) + break; + } + if (proto == NULL) { + errno = EINVAL; + return (-1); + } + + newconn = proto_alloc(proto, + client ? PROTO_SIDE_CLIENT : PROTO_SIDE_SERVER_WORK); + if (newconn == NULL) + return (-1); + PJDLOG_ASSERT(newconn->pc_proto->prt_wrap != NULL); + error = newconn->pc_proto->prt_wrap(fd, client, &newconn->pc_ctx); + if (error != 0) { + proto_free(newconn); + errno = error; + return (-1); + } + + *newconnp = newconn; + + return (0); +} + +int +proto_connection_recv(const struct proto_conn *conn, bool client, + struct proto_conn **newconnp) +{ + char protoname[128]; + int error, fd; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL); + PJDLOG_ASSERT(newconnp != NULL); + + bzero(protoname, sizeof(protoname)); + + error = conn->pc_proto->prt_recv(conn->pc_ctx, + (unsigned char *)protoname, sizeof(protoname) - 1, &fd); + if (error != 0) { + errno = error; + return (-1); + } + + PJDLOG_ASSERT(fd >= 0); + + return (proto_wrap(protoname, client, fd, newconnp)); +} + +int +proto_descriptor(const struct proto_conn *conn) +{ + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_descriptor != NULL); + + return (conn->pc_proto->prt_descriptor(conn->pc_ctx)); +} + +bool +proto_address_match(const struct proto_conn *conn, const char *addr) +{ + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_address_match != NULL); + + return (conn->pc_proto->prt_address_match(conn->pc_ctx, addr)); +} + +void +proto_local_address(const struct proto_conn *conn, char *addr, size_t size) +{ + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_local_address != NULL); + + conn->pc_proto->prt_local_address(conn->pc_ctx, addr, size); +} + +void +proto_remote_address(const struct proto_conn *conn, char *addr, size_t size) +{ + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_remote_address != NULL); + + conn->pc_proto->prt_remote_address(conn->pc_ctx, addr, size); +} + +int +proto_timeout(const struct proto_conn *conn, int timeout) +{ + struct timeval tv; + int fd; + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + + fd = proto_descriptor(conn); + if (fd < 0) + return (-1); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) + return (-1); + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + return (-1); + + return (0); +} + +void +proto_close(struct proto_conn *conn) +{ + + PJDLOG_ASSERT(conn != NULL); + PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); + PJDLOG_ASSERT(conn->pc_proto != NULL); + PJDLOG_ASSERT(conn->pc_proto->prt_close != NULL); + + conn->pc_proto->prt_close(conn->pc_ctx); + proto_free(conn); +} + +int +proto_exec(int argc, char *argv[]) +{ + struct proto *proto; + int error; + + if (argc == 0) { + errno = EINVAL; + return (-1); + } + TAILQ_FOREACH(proto, &protos, prt_next) { + if (strcmp(proto->prt_name, argv[0]) == 0) + break; + } + if (proto == NULL) { + errno = EINVAL; + return (-1); + } + if (proto->prt_exec == NULL) { + errno = EOPNOTSUPP; + return (-1); + } + error = proto->prt_exec(argc, argv); + if (error != 0) { + errno = error; + return (-1); + } + /* NOTREACHED */ + return (0); +} + +struct proto_nvpair { + char *pnv_name; + char *pnv_value; + TAILQ_ENTRY(proto_nvpair) pnv_next; +}; + +static TAILQ_HEAD(, proto_nvpair) proto_nvpairs = + TAILQ_HEAD_INITIALIZER(proto_nvpairs); + +int +proto_set(const char *name, const char *value) +{ + struct proto_nvpair *pnv; + + TAILQ_FOREACH(pnv, &proto_nvpairs, pnv_next) { + if (strcmp(pnv->pnv_name, name) == 0) + break; + } + if (pnv != NULL) { + TAILQ_REMOVE(&proto_nvpairs, pnv, pnv_next); + free(pnv->pnv_value); + } else { + pnv = malloc(sizeof(*pnv)); + if (pnv == NULL) + return (-1); + pnv->pnv_name = strdup(name); + if (pnv->pnv_name == NULL) { + free(pnv); + return (-1); + } + } + pnv->pnv_value = strdup(value); + if (pnv->pnv_value == NULL) { + free(pnv->pnv_name); + free(pnv); + return (-1); + } + TAILQ_INSERT_TAIL(&proto_nvpairs, pnv, pnv_next); + return (0); +} + +const char * +proto_get(const char *name) +{ + struct proto_nvpair *pnv; + + TAILQ_FOREACH(pnv, &proto_nvpairs, pnv_next) { + if (strcmp(pnv->pnv_name, name) == 0) + break; + } + if (pnv != NULL) + return (pnv->pnv_value); + return (NULL); +} |