-/*
- * Return a connected stream. For ssh, this means setup a stream
- * with the supplied handle.
- */
-static void *
-ssh_stream_client(h, id)
- void *h;
- int id;
-{
- struct ssh_handle *rh = h;
- struct ssh_stream *rs;
-
- assert(rh != NULL);
-
- if (id <= 0) {
- security_seterror(&rh->sech,
- "%d: invalid security stream id", id);
- return (NULL);
- }
-
- rs = alloc(sizeof(*rs));
- security_streaminit(&rs->secstr, &ssh_security_driver);
- rs->handle = id;
- rs->ev_read = NULL;
- rs->rc = conn_get(rh->hostname);
-
- sshprintf(("%s: ssh: stream_client: connected to stream %d\n", debug_prefix_time(NULL), id));
-
- return (rs);
-}
-
-/*
- * Close and unallocate resources for a stream.
- */
-static void
-ssh_stream_close(s)
- void *s;
-{
- struct ssh_stream *rs = s;
-
- assert(rs != NULL);
-
- sshprintf(("%s: ssh: stream_close: closing stream %d\n", debug_prefix_time(NULL), rs->handle));
-
- ssh_stream_read_cancel(rs);
- conn_put(rs->rc);
- amfree(rs);
-}
-
-/*
- * Authenticate a stream
- * Nothing needed for ssh. The connection is authenticated by sshd
- * on startup.
- */
-static int
-ssh_stream_auth(s)
- void *s;
-{
-
- return (0);
-}
-
-/*
- * Returns the stream id for this stream. This is just the local
- * port.
- */
-static int
-ssh_stream_id(s)
- void *s;
-{
- struct ssh_stream *rs = s;
-
- assert(rs != NULL);
-
- return (rs->handle);
-}
-
-/*
- * Write a chunk of data to a stream. Blocks until completion.
- */
-static int
-ssh_stream_write(s, buf, size)
- void *s;
- const void *buf;
- size_t size;
-{
- struct ssh_stream *rs = s;
-
- assert(rs != NULL);
-
- sshprintf(("%s: ssh: stream_write: writing %d bytes to %s:%d\n", debug_prefix_time(NULL), size,
- rs->rc->hostname, rs->handle));
-
- if (send_token(rs->rc, rs->handle, buf, size) < 0) {
- security_stream_seterror(&rs->secstr, rs->rc->errmsg);
- return (-1);
- }
- return (0);
-}
-
-/*
- * Submit a request to read some data. Calls back with the given
- * function and arg when completed.
- */
-static void
-ssh_stream_read(s, fn, arg)
- void *s, *arg;
- void (*fn) P((void *, void *, ssize_t));
-{
- struct ssh_stream *rs = s;
-
- assert(rs != NULL);
-
- /*
- * Only one read request can be active per stream.
- */
- if (rs->ev_read == NULL) {
- rs->ev_read = event_register((event_id_t)rs->rc, EV_WAIT,
- stream_read_callback, rs);
- conn_read(rs->rc);
- }
- rs->fn = fn;
- rs->arg = arg;
-}
-
-/*
- * Cancel a previous stream read request. It's ok if we didn't have a read
- * scheduled.
- */
-static void
-ssh_stream_read_cancel(s)
- void *s;
-{
- struct ssh_stream *rs = s;
-
- assert(rs != NULL);
-
- if (rs->ev_read != NULL) {
- event_release(rs->ev_read);
- rs->ev_read = NULL;
- conn_read_cancel(rs->rc);
- }
-}
-
-/*
- * Callback for ssh_stream_read
- */
-static void
-stream_read_callback(arg)
- void *arg;
-{
- struct ssh_stream *rs = arg;
- assert(rs != NULL);
-
- sshprintf(("%s: ssh: stream_read_callback: handle %d\n", debug_prefix_time(NULL), rs->handle));
-
- /*
- * Make sure this was for us. If it was, then blow away the handle
- * so it doesn't get claimed twice. Otherwise, leave it alone.
- *
- * If the handle is EOF, pass that up to our callback.
- */
- if (rs->rc->handle == rs->handle) {
- sshprintf(("%s: ssh: stream_read_callback: it was for us\n", debug_prefix_time(NULL)));
- rs->rc->handle = H_TAKEN;
- } else if (rs->rc->handle != H_EOF) {
- sshprintf(("%s: ssh: stream_read_callback: not for us\n", debug_prefix_time(NULL)));
- return;
- }
-
- /*
- * Remove the event first, and then call the callback.
- * We remove it first because we don't want to get in their
- * way if they reschedule it.
- */
- ssh_stream_read_cancel(rs);
-
- if (rs->rc->pktlen == 0) {
- sshprintf(("%s: ssh: stream_read_callback: EOF\n", debug_prefix_time(NULL)));
- (*rs->fn)(rs->arg, NULL, 0);
- return;
- }
- sshprintf(("%s: ssh: stream_read_callback: read %ld bytes from %s:%d\n", debug_prefix_time(NULL),
- rs->rc->pktlen, rs->rc->hostname, rs->handle));
- (*rs->fn)(rs->arg, rs->rc->pkt, rs->rc->pktlen);
-}
-
-/*
- * The callback for the netfd for the event handler
- * Determines if this packet is for this security handle,
- * and does the real callback if so.
- */
-static void
-conn_read_callback(cookie)
- void *cookie;
-{
- struct ssh_conn *rc = cookie;
- struct ssh_handle *rh;
- pkt_t pkt;
- int rval;
-
- assert(cookie != NULL);
-
- sshprintf(("%s: ssh: conn_read_callback\n",debug_prefix_time(NULL)));
-
- /* Read the data off the wire. If we get errors, shut down. */
- rval = recv_token(rc, 60);
- sshprintf(("%s: ssh: conn_read_callback: recv_token returned %d\n", debug_prefix_time(NULL), rval));
- if (rval <= 0) {
- rc->pktlen = 0;
- rc->handle = H_EOF;
- rval = event_wakeup((event_id_t)rc);
- sshprintf(("%s: ssh: conn_read_callback: event_wakeup return %d\n", debug_prefix_time(NULL), rval));
- /* delete our 'accept' reference */
- if (accept_fn != NULL)
- conn_put(rc);
- accept_fn = NULL;
- return;
- }
-
- /* If there are events waiting on this handle, we're done */
- rval = event_wakeup((event_id_t)rc);
- sshprintf(("%s: ssh: conn_read_callback: event_wakeup return %d\n", debug_prefix_time(NULL), rval));
- if (rval > 0)
- return;
-
- /* If there is no accept fn registered, then drop the packet */
- if (accept_fn == NULL)
- return;
-
- rh = alloc(sizeof(*rh));
- security_handleinit(&rh->sech, &ssh_security_driver);
- rh->hostname = rc->hostname;
- rh->rs = ssh_stream_client(rh, rc->handle);
- rh->ev_timeout = NULL;
-
- sshprintf(("%s: ssh: new connection\n", debug_prefix_time(NULL)));
- parse_pkt(&pkt, rc->pkt, rc->pktlen);
- sshprintf(("%s: ssh: calling accept_fn\n", debug_prefix_time(NULL)));
- (*accept_fn)(&rh->sech, &pkt);
-}
-
-static void
-parse_pkt(pkt, buf, bufsize)
- pkt_t *pkt;
- const void *buf;
- size_t bufsize;
-{
- const unsigned char *bufp = buf;
-
- sshprintf(("%s: ssh: parse_pkt: parsing buffer of %d bytes\n", debug_prefix_time(NULL), bufsize));
-
- pkt->type = (pktype_t)*bufp++;
- bufsize--;
-
- if (bufsize == 0) {
- pkt->body[0] = '\0';
- } else {
- if (bufsize > sizeof(pkt->body) - 1)
- bufsize = sizeof(pkt->body) - 1;
- memcpy(pkt->body, bufp, bufsize);
- pkt->body[sizeof(pkt->body) - 1] = '\0';
- }
-
- sshprintf(("%s: ssh: parse_pkt: %s (%d): \"%s\"\n", debug_prefix_time(NULL), pkt_type2str(pkt->type),
- pkt->type, pkt->body));
-}
-
-
-/*
- * Transmits a chunk of data over a ssh_handle, adding
- * the necessary headers to allow the remote end to decode it.
- */
-static int
-send_token(rc, handle, buf, len)
- struct ssh_conn *rc;
- int handle;
- const void *buf;
- size_t len;
-{
- unsigned int netlength, nethandle;
- struct iovec iov[3];
-
- sshprintf(("%s: ssh: send_token: handle %d writing %d bytes to %s\n", debug_prefix_time(NULL), handle, len,
- rc->hostname));
-
- assert(sizeof(netlength) == 4);
-
- /*
- * Format is:
- * 32 bit length (network byte order)
- * 32 bit handle (network byte order)
- * data
- */
- netlength = htonl(len);
- iov[0].iov_base = (void *)&netlength;
- iov[0].iov_len = sizeof(netlength);
-
- nethandle = htonl(handle);
- iov[1].iov_base = (void *)&nethandle;
- iov[1].iov_len = sizeof(nethandle);
-
- iov[2].iov_base = (void *)buf;
- iov[2].iov_len = len;
-
- if (net_writev(rc->write, iov, 3) < 0) {
- rc->errmsg = newvstralloc(rc->errmsg, "ssh write error to ",
- rc->hostname, ": ", strerror(errno), NULL);
- return (-1);
- }
- return (0);
-}
-
-static int
-recv_token(rc, timeout)
- struct ssh_conn *rc;
- int timeout;
-{
- unsigned int netint;
-
- assert(sizeof(netint) == 4);
-
- assert(rc->read >= 0);
-
- sshprintf(("%s: ssh: recv_token: reading from %s\n", debug_prefix_time(NULL), rc->hostname));
-
- switch (net_read(rc, &netint, sizeof(netint), timeout)) {
- case -1:
- rc->errmsg = newvstralloc(rc->errmsg, "recv error: ", strerror(errno),
- NULL);
- sshprintf(("%s: ssh: recv_token: A return(-1)\n", debug_prefix_time(NULL)));
- return (-1);
- case 0:
- rc->pktlen = 0;
- sshprintf(("%s: ssh: recv_token: A return(0)\n", debug_prefix_time(NULL)));
- return (0);
- default:
- break;
- }
- rc->pktlen = ntohl(netint);
- if (rc->pktlen > sizeof(rc->pkt)) {
- rc->errmsg = newstralloc(rc->errmsg, "recv error: huge packet");
- sshprintf(("%s: ssh: recv_token: B return(-1)\n", debug_prefix_time(NULL)));
- return (-1);
- }
-
- switch (net_read(rc, &netint, sizeof(netint), timeout)) {
- case -1:
- rc->errmsg = newvstralloc(rc->errmsg, "recv error: ", strerror(errno),
- NULL);
- sshprintf(("%s: ssh: recv_token: C return(-1)\n", debug_prefix_time(NULL)));
- return (-1);
- case 0:
- rc->pktlen = 0;
- sshprintf(("%s: ssh: recv_token: D return(0)\n", debug_prefix_time(NULL)));
- return (0);
- default:
- break;
- }
- rc->handle = ntohl(netint);
-
- switch (net_read(rc, rc->pkt, rc->pktlen, timeout)) {
- case -1:
- rc->errmsg = newvstralloc(rc->errmsg, "recv error: ", strerror(errno),
- NULL);
- sshprintf(("%s: ssh: recv_token: E return(-1)\n", debug_prefix_time(NULL)));
- return (-1);
- case 0:
- rc->pktlen = 0;
- break;
- default:
- break;
- }
-
- sshprintf(("%s: ssh: recv_token: read %ld bytes from %s\n", debug_prefix_time(NULL), rc->pktlen,
- rc->hostname));
- sshprintf(("%s: ssh: recv_token: end %d\n", debug_prefix_time(NULL),rc->pktlen));
- return (rc->pktlen);
-}
-
-/*
- * Writes out the entire iovec
- */
-static int
-net_writev(fd, iov, iovcnt)
- int fd, iovcnt;
- struct iovec *iov;
-{
- int delta, n, total;
-
- assert(iov != NULL);
-
- total = 0;
- while (iovcnt > 0) {
- /*
- * Write the iovec
- */
- total += n = writev(fd, iov, iovcnt);
- if (n < 0)
- return (-1);
- if (n == 0) {
- errno = EIO;
- return (-1);
- }
- /*
- * Iterate through each iov. Figure out what we still need
- * to write out.
- */
- for (; n > 0; iovcnt--, iov++) {
- /* 'delta' is the bytes written from this iovec */
- delta = n < iov->iov_len ? n : iov->iov_len;
- /* subtract from the total num bytes written */
- n -= delta;
- assert(n >= 0);
- /* subtract from this iovec */
- iov->iov_len -= delta;
- iov->iov_base = (char *)iov->iov_base + delta;
- /* if this iovec isn't empty, run the writev again */
- if (iov->iov_len > 0)
- break;
- }
- }
- return (total);
-}
-
-/*
- * Like read(), but waits until the entire buffer has been filled.
- */
-static ssize_t
-net_read(rc, vbuf, origsize, timeout)
- struct ssh_conn *rc;
- void *vbuf;
- size_t origsize;
- int timeout;
-{
- char *buf = vbuf, *off; /* ptr arith */
- int nread;
- size_t size = origsize;
-
- sshprintf(("%s: ssh: net_read: begin %d\n", debug_prefix_time(NULL), origsize));
- while (size > 0) {
- sshprintf(("%s: ssh: net_read: while %d\n", debug_prefix_time(NULL), size));
- if (rc->readbuf.left == 0) {
- if (net_read_fillbuf(rc, timeout, size) < 0) {
- sshprintf(("%s: ssh: net_read: end retrun(-1)\n", debug_prefix_time(NULL)));
- return (-1);
- }
- if (rc->readbuf.size == 0) {
- sshprintf(("%s: ssh: net_read: end retrun(0)\n", debug_prefix_time(NULL)));
- return (0);
- }
- }
- nread = min(rc->readbuf.left, size);
- off = rc->readbuf.buf + rc->readbuf.size - rc->readbuf.left;
- memcpy(buf, off, nread);
-
- buf += nread;
- size -= nread;
- rc->readbuf.left -= nread;
- }
- sshprintf(("%s: ssh: net_read: end %d\n", debug_prefix_time(NULL), origsize));
- return ((ssize_t)origsize);
-}
-
-/*
- * net_read likes to do a lot of little reads. Buffer it.
- */
-static int
-net_read_fillbuf(rc, timeout, size)
- struct ssh_conn *rc;
- int timeout;
- int size;
-{
- fd_set readfds;
- struct timeval tv;
- if(size > sizeof(rc->readbuf.buf)) size = sizeof(rc->readbuf.buf);
-
- sshprintf(("%s: ssh: net_read_fillbuf: begin\n", debug_prefix_time(NULL)));
- FD_ZERO(&readfds);
- FD_SET(rc->read, &readfds);
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
- switch (select(rc->read + 1, &readfds, NULL, NULL, &tv)) {
- case 0:
- errno = ETIMEDOUT;
- /* FALLTHROUGH */
- case -1:
- sshprintf(("%s: ssh: net_read_fillbuf: case -1\n", debug_prefix_time(NULL)));
- return (-1);
- case 1:
- sshprintf(("%s: ssh: net_read_fillbuf: case 1\n", debug_prefix_time(NULL)));
- assert(FD_ISSET(rc->read, &readfds));
- break;
- default:
- sshprintf(("%s: ssh: net_read_fillbuf: case default\n", debug_prefix_time(NULL)));
- assert(0);
- break;
- }
- rc->readbuf.left = 0;
- rc->readbuf.size = read(rc->read, rc->readbuf.buf, size);
- if (rc->readbuf.size < 0)
- return (-1);
- rc->readbuf.left = rc->readbuf.size;
- sshprintf(("%s: ssh: net_read_fillbuf: end %d\n", debug_prefix_time(NULL),rc->readbuf.size));
- return (0);