root/usr/src/cmd/dlutil/dlrecv.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 */

/*
 * Receive a raw Ethernet frame from dlsend.
 */

#include <stdio.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <stdarg.h>
#include <libgen.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <libdlpi.h>
#include <stddef.h>
#include <stdint.h>
#include <endian.h>
#include <ctype.h>
#include <err.h>

#include "dlsend.h"


static uint_t dlrecv_sap = DLSEND_SAP;
static const char *dlrecv_prog;

static void
dlrecv_usage(const char *fmt, ...)
{
        if (fmt != NULL) {
                va_list ap;

                (void) fprintf(stderr, "%s: ", dlrecv_prog);
                va_start(ap, fmt);
                (void) vfprintf(stderr, fmt, ap);
                va_end(ap);
        }

        (void) fprintf(stderr, "Usage: %s [-s sap] device\n"
            "\t-s sap\tspecify SAP to send on\n",
            dlrecv_prog);
}

static boolean_t
dlrecv_isvalid(dlsend_msg_t *msg)
{
        uint_t i;
        boolean_t nul;

        nul = B_FALSE;
        for (i = 0; i < sizeof (msg->dm_host); i++) {
                if (!isprint(msg->dm_host[i]) &&
                    msg->dm_host[i] != '\0') {
                        warnx("Encountered bad byte in dm_host[%d]",
                            i);
                        return (B_FALSE);
                }

                if (msg->dm_host[i] == '\0')
                        nul = B_TRUE;
        }

        if (!nul) {
                warnx("Missing NUL in dm_host");
                return (B_FALSE);
        }

        nul = B_FALSE;
        for (i = 0; i < sizeof (msg->dm_mesg); i++) {
                if (!isprint(msg->dm_mesg[i]) &&
                    msg->dm_mesg[i] != '\0') {
                        warnx("Encountered bad byte in dm_mesg[%d]",
                            i);
                        return (B_FALSE);
                }

                if (msg->dm_mesg[i] == '\0')
                        nul = B_TRUE;
        }

        if (!nul) {
                warnx("Missing NUL in dm_mesg");
                return (B_FALSE);
        }

        if (strcmp(msg->dm_mesg, DLSEND_MSG) != 0) {
                warnx("Missing expected message (%s)", DLSEND_MSG);
                return (B_FALSE);
        }

        return (B_TRUE);
}

static void
dlrecv_print(dlsend_msg_t *msg, dlpi_recvinfo_t *rinfo, boolean_t invalid)
{
        uint_t i;

        (void) printf("Received %s from ", invalid ?
            "invalid message" : "Elbereth");

        for (i = 0; i < rinfo->dri_destaddrlen; i++) {
                (void) printf("%02x", rinfo->dri_destaddr[i]);
                if (i + 1 != rinfo->dri_destaddrlen)
                        (void) putchar(':');
        }

        if (invalid) {
                return;
        }

        (void) printf(" seq=%" PRIu64 " host=%s\n", betoh64(msg->dm_count),
            msg->dm_host);
}

int
main(int argc, char *argv[])
{
        int c, ret;
        char *eptr;
        unsigned long sap;
        uint_t bind_sap;
        dlpi_handle_t dh;

        dlrecv_prog = basename(argv[0]);

        while ((c = getopt(argc, argv, ":s:")) != -1) {
                switch (c) {
                case 's':
                        errno = 0;
                        sap = strtoul(optarg, &eptr, 10);
                        if (errno != 0 || sap == 0 || sap >= UINT16_MAX ||
                            *eptr != '\0') {
                                dlrecv_usage("Invalid value for sap (-s): %s\n",
                                    optarg);
                                return (2);
                        }
                        dlrecv_sap = sap;
                        break;
                case ':':
                        dlrecv_usage("Option -%c requires an operand\n",
                            optopt);
                        return (2);
                case '?':
                        dlrecv_usage("Unknown option: -%c\n", optopt);
                        return (2);
                }
        }

        argc -= optind;
        argv += optind;

        if (argc != 1) {
                dlrecv_usage("missing required operands\n");
                return (2);
        }

        if ((ret = dlpi_open(argv[0], &dh, 0)) != DLPI_SUCCESS) {
                warnx("failed to open %s: %s", argv[0],
                    dlpi_strerror(ret));
                exit(1);
        }

        if ((ret = dlpi_bind(dh, dlrecv_sap, &bind_sap)) != DLPI_SUCCESS) {
                warnx("failed to bind to sap 0x%x: %s", dlrecv_sap,
                    dlpi_strerror(ret));
                exit(1);
        }

        if (bind_sap != dlrecv_sap) {
                warnx("failed to bind to requested sap 0x%x, bound to "
                    "0x%x", dlrecv_sap, bind_sap);
                exit(1);
        }

        for (;;) {
                dlpi_recvinfo_t rinfo;
                dlsend_msg_t msg;
                size_t msglen;
                boolean_t invalid = B_FALSE;

                msglen = sizeof (msg);
                ret = dlpi_recv(dh, NULL, NULL, &msg, &msglen, -1, &rinfo);
                if (ret != DLPI_SUCCESS) {
                        warnx("failed to receive data: %s", dlpi_strerror(ret));
                        continue;
                }

                if (msglen != rinfo.dri_totmsglen) {
                        warnx("message truncated: expected %zu bytes, "
                            "got %zu", sizeof (dlsend_msg_t),
                            rinfo.dri_totmsglen);
                        invalid = B_TRUE;
                }

                if (msglen != sizeof (msg)) {
                        warnx("message too short: expected %zu bytes, "
                            "got %zu", sizeof (dlsend_msg_t), msglen);
                        invalid = B_TRUE;
                }

                if (!invalid) {
                        invalid = !dlrecv_isvalid(&msg);
                }

                dlrecv_print(&msg, &rinfo, invalid);
        }

        /* LINTED: E_STMT_NOT_REACHED */
        return (0);
}