root/usr/src/lib/smbclnt/libfknsmb/common/fake_ktli.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
 * Copyright 2024 RackTop Systems, Inc.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */

/*
 * Kernel TLI-like functions
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/tihdr.h>
#include <sys/timod.h>
#include <sys/tiuser.h>
#include <sys/t_kuser.h>

#include <errno.h>
#include <stropts.h>
#include <unistd.h>

/*
 * While <xti.h> is the perferred interface, we're simulating the
 * (in-kernel) kTLI here, which forces sys/tiuser.h on us, so just
 * use the older <tiuser.h> for the simulation.
 */
#include <tiuser.h>

#include "fake_fio.h"

/* Size of mblks for tli_recv */
#define FKTLI_RCV_SZ    4096

/*
 * Translate a TLI error into a system error as best we can.
 */
static const int tli_errs[] = {
        0,              /* no error     */
        EADDRNOTAVAIL,  /* TBADADDR     */
        ENOPROTOOPT,    /* TBADOPT      */
        EACCES,         /* TACCES       */
        EBADF,          /* TBADF        */
        EADDRNOTAVAIL,  /* TNOADDR      */
        EPROTO,         /* TOUTSTATE    */
        EPROTO,         /* TBADSEQ      */
        ENOSYS,         /* TSYSERR      */
        EPROTO,         /* TLOOK        */
        EMSGSIZE,       /* TBADDATA     */
        EMSGSIZE,       /* TBUFOVFLW    */
        EPROTO,         /* TFLOW        */
        EWOULDBLOCK,    /* TNODATA      */
        EPROTO,         /* TNODIS       */
        EPROTO,         /* TNOUDERR     */
        EINVAL,         /* TBADFLAG     */
        EPROTO,         /* TNOREL       */
        EOPNOTSUPP,     /* TNOTSUPPORT  */
        EPROTO,         /* TSTATECHNG   */
};

static int
tlitosyserr(int terr)
{
        if (terr < 0 || (terr >= (sizeof (tli_errs) / sizeof (tli_errs[0]))))
                return (EPROTO);
        else
                return (tli_errs[terr]);
}

/*
 * Note: This implementation is specific to the needs of the callers in
 * uts/common/fs/smbclnt/netsmb/smb_trantcp.c
 */
/* ARGSUSED */
int
t_kopen(file_t *fp, dev_t rdev, int flags, TIUSER **tiptr, cred_t *cr)
{
        boolean_t madefp = B_FALSE;
        TIUSER *tiu;
        int fd;
        int rc;

        *tiptr = NULL;

        if (fp == NULL) {
                /*
                 * create a socket endpoint
                 * dev is actualy AF
                 */
                char *devnm;
                switch (rdev) {
                case AF_INET:
                        devnm = "/dev/tcp";
                        break;
                case AF_INET6:
                        devnm = "/dev/tcp6";
                        break;
                default:
                        cmn_err(CE_NOTE, "t_kopen: bad device");
                        return (EINVAL);
                }

                fd = t_open(devnm, O_RDWR, NULL);
                if (fd < 0) {
                        rc = t_errno;
                        cmn_err(CE_NOTE, "t_kopen: t_open terr=%d", rc);
                        return (tlitosyserr(rc));
                }

                /*
                 * allocate a file pointer...
                 */
                fp = file_getf(fd);
                madefp = B_TRUE;
        }
        fd = file_getfd(fp);

        tiu = kmem_zalloc(sizeof (*tiu), KM_SLEEP);
        rc = t_getinfo(fd, &tiu->tp_info);
        if (rc < 0) {
                rc = t_errno;
                cmn_err(CE_NOTE, "t_kopen: t_getinfo terr=%d", rc);
                kmem_free(tiu, sizeof (*tiu));
                if (madefp) {
                        file_releasef(fd);
                        (void) t_close(fd);
                }
                return (tlitosyserr(rc));
        }

        tiu->fp = fp;
        tiu->flags = madefp ? MADE_FP : 0;
        *tiptr = tiu;

        return (0);
}

/* ARGSUSED */
int
t_kclose(TIUSER *tiptr, int callclosef)
{
        file_t  *fp;

        fp = (tiptr->flags & MADE_FP) ? tiptr->fp : NULL;

        kmem_free(tiptr, TIUSERSZ);

        if (fp != NULL) {
                int fd = file_getfd(fp);
                file_releasef(fd);
                (void) t_close(fd);
        }

        return (0);
}

int
t_kbind(TIUSER *tiptr, struct t_bind *req, struct t_bind *ret)
{
        file_t          *fp = tiptr->fp;
        int             fd = file_getfd(fp);
        int             rc;

        if (t_bind(fd, req, ret) < 0) {
                rc = t_errno;
                cmn_err(CE_NOTE, "t_kbind: t_bind terr=%d", rc);
                return (tlitosyserr(rc));
        }
        return (0);
}

int
t_kunbind(TIUSER *tiptr)
{
        file_t          *fp = tiptr->fp;
        int             fd = file_getfd(fp);
        int             rc;

        if (t_unbind(fd) < 0) {
                rc = t_errno;
                cmn_err(CE_NOTE, "t_kunbind: t_unbind terr=%d", rc);
                return (tlitosyserr(rc));
        }
        return (0);
}

int
t_kconnect(TIUSER *tiptr, struct t_call *sndcall, struct t_call *rcvcall)
{
        file_t          *fp = tiptr->fp;
        int             fd = file_getfd(fp);
        int             rc;

        if (t_connect(fd, sndcall, rcvcall) < 0) {
                rc = t_errno;
                cmn_err(CE_NOTE, "t_kconnect: t_connect terr=%d", rc);
                if (rc == TLOOK) {
                        /* Probably got a RST. */
                        rc = ECONNREFUSED;
                } else {
                        rc = tlitosyserr(rc);
                }
                return (rc);
        }
        return (0);
}

int
t_koptmgmt(TIUSER *tiptr, struct t_optmgmt *req, struct t_optmgmt *ret)
{
        file_t          *fp = tiptr->fp;
        int             fd = file_getfd(fp);
        int             rc;

        if (t_optmgmt(fd, req, ret) < 0) {
                rc = t_errno;
                cmn_err(CE_NOTE, "t_koptmgmt: t_optmgmt terr=%d", rc);
                return (tlitosyserr(rc));
        }
        return (0);
}

/*
 * Poll for an input event.
 *
 * timo is measured in ticks
 */
int
t_kspoll(TIUSER *tiptr, int timo, int waitflg, int *events)
{
        struct pollfd   pfds[1];
        file_t          *fp = tiptr->fp;
        int             fd = file_getfd(fp);
        clock_t         timout; /* milliseconds */
        int             n;

        if (events == NULL || ((waitflg & READWAIT) == 0))
                return (EINVAL);

        /* Convert from ticks to milliseconds */
        if (timo < 0)
                timout = -1;
        else
                timout = TICK_TO_MSEC(timo);

        pfds[0].fd = fd;
        pfds[0].events = POLLIN;
        pfds[0].revents = 0;

        errno = 0;
        n = poll(pfds, 1, timout);
        if (n < 0)
                return (errno);
        if (n == 0)
                return (ETIME);
        *events = pfds[0].revents;
        return (0);
}

/*
 * Send the message, return zero or errno.
 * Always free's the message, even on error.
 */
int
tli_send(TIUSER *tiptr, mblk_t *bp, int fmode)
{
        struct strbuf ctlbuf;
        struct strbuf databuf;
        mblk_t  *m;
        int     flg, n, rc;
        file_t  *fp = tiptr->fp;
        int     fd = file_getfd(fp);

        if (bp == NULL)
                return (0);

        switch (bp->b_datap->db_type) {
        case M_DATA:
                for (m = bp; m != NULL; m = m->b_cont) {
                        n = MBLKL(m);
                        flg = (m->b_cont != NULL) ? T_MORE : 0;
                        rc = t_snd(fd, (void *) m->b_rptr, n, flg);
                        if (rc != n) {
                                rc = EIO;
                                goto out;
                        }
                }
                rc = 0;
                break;

        /*
         * May get M_PROTO/T_DISCON_REQ from nb_snddis()
         */
        case M_PROTO:
        case M_PCPROTO:
                ctlbuf.len = MBLKL(bp);
                ctlbuf.maxlen = MBLKL(bp);
                ctlbuf.buf = (char *)bp->b_rptr;
                if (bp->b_cont == NULL) {
                        bzero(&databuf, sizeof (databuf));
                } else {
                        m = bp->b_cont;
                        databuf.len = MBLKL(m);
                        databuf.maxlen = MBLKL(m);
                        databuf.buf = (char *)m->b_rptr;
                }
                if (putmsg(fd, &ctlbuf, &databuf, 0) < 0) {
                        rc = errno;
                        cmn_err(CE_NOTE, "tli_send: putmsg err=%d", rc);
                } else {
                        rc = 0;
                }
                break;

        default:
                rc = EIO;
                break;
        }

out:
        freemsg(bp);
        return (rc);
}

int
tli_recv(TIUSER *tiptr, mblk_t **bp, int fmode)
{
        mblk_t          *mtop = NULL;
        mblk_t          *m;
        file_t          *fp = tiptr->fp;
        int             fd = file_getfd(fp);
        int             error;
        int             flags;
        int             nread;
        int             n;

        /*
         * Get an mblk for the data
         */
        nread = FKTLI_RCV_SZ;
        m = allocb_wait(nread, 0, 0, &error);
        ASSERT(m != NULL);

        if (mtop == NULL)
                mtop = m;

again:
        flags = 0;
        n = t_rcv(fd, (void *) m->b_rptr, nread, &flags);
        if (n < 0) {
                n = t_errno;
                cmn_err(CE_NOTE, "tli_recv: t_rcv terr=%d", n);
                error = tlitosyserr(n);
                goto errout;
        }
        if (n == 0) {
                error = ENOTCONN;
                goto errout;
        }
        ASSERT(n > 0 && n <= nread);
        m->b_wptr = m->b_rptr + n;

        if (flags & T_MORE) {
                mblk_t  *mtail = m;
                m = allocb_wait(nread, 0, 0, &error);
                ASSERT(m != NULL);
                mtail->b_cont = m;
                goto again;
        }

        *bp = mtop;
        return (0);

errout:
        if (m == mtop) {
                freemsg(mtop);
                return (error);
        }

        /* got some data, so return it. */
        return (0);
}