Esempio n. 1
0
/* Sets conn->state to READING when done. */
static krb5_boolean
service_tcp_write(krb5_context context, const krb5_data *realm,
                  struct conn_state *conn, struct select_state *selstate)
{
    ssize_t nwritten;
    SOCKET_WRITEV_TEMP tmp;

    TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr);
    nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp);
    if (nwritten < 0) {
        TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO);
        kill_conn(context, conn, selstate);
        return FALSE;
    }
    while (nwritten) {
        sg_buf *sgp = conn->out.sgp;
        if ((size_t)nwritten < SG_LEN(sgp)) {
            SG_ADVANCE(sgp, (size_t)nwritten);
            nwritten = 0;
        } else {
            nwritten -= SG_LEN(sgp);
            conn->out.sgp++;
            conn->out.sg_count--;
        }
    }
    if (conn->out.sg_count == 0) {
        /* Done writing, switch to reading. */
        cm_read(selstate, conn->fd);
        conn->state = READING;
    }
    return FALSE;
}
Esempio n. 2
0
static int
service_tcp_fd(krb5_context context, struct conn_state *conn,
               struct select_state *selstate, int ssflags)
{
    int e = 0;
    ssize_t nwritten, nread;

    if (!(ssflags & (SSF_READ|SSF_WRITE|SSF_EXCEPTION)))
        abort();
    switch (conn->state) {
        SOCKET_WRITEV_TEMP tmp;

    case CONNECTING:
        if (ssflags & SSF_READ) {
            /* Bad -- the KDC shouldn't be sending to us first.  */
            e = EINVAL /* ?? */;
        kill_conn:
            TRACE_SENDTO_KDC_TCP_DISCONNECT(context, conn);
            kill_conn(conn, selstate, e);
            if (e == EINVAL) {
                closesocket(conn->fd);
                conn->fd = INVALID_SOCKET;
            }
            return e == 0;
        }
        if (ssflags & SSF_EXCEPTION) {
        handle_exception:
            e = get_so_error(conn->fd);
            if (e)
                dprint("socket error on exception fd: %m", e);
            else
                dprint("no socket error info available on exception fd");
            goto kill_conn;
        }

        /*
         * Connect finished -- but did it succeed or fail?
         * UNIX sets can_write if failed.
         * Call getsockopt to see if error pending.
         *
         * (For most UNIX systems it works to just try writing the
         * first time and detect an error.  But Bill Dodd at IBM
         * reports that some version of AIX, SIGPIPE can result.)
         */
        e = get_so_error(conn->fd);
        if (e) {
            TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, conn, e);
            dprint("socket error on write fd: %m", e);
            goto kill_conn;
        }
        conn->state = WRITING;
        goto try_writing;

    case WRITING:
        if (ssflags & SSF_READ) {
            e = E2BIG;
            /* Bad -- the KDC shouldn't be sending anything yet.  */
            goto kill_conn;
        }
        if (ssflags & SSF_EXCEPTION)
            goto handle_exception;

    try_writing:
        dprint("trying to writev %d (%d bytes) to fd %d\n",
               conn->x.out.sg_count,
               ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
                + SG_LEN(&conn->x.out.sgp[0])),
               conn->fd);
        TRACE_SENDTO_KDC_TCP_SEND(context, conn);
        nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
                                 conn->x.out.sg_count, tmp);
        if (nwritten < 0) {
            e = SOCKET_ERRNO;
            TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, conn, e);
            dprint("failed: %m\n", e);
            goto kill_conn;
        }
        dprint("wrote %d bytes\n", nwritten);
        while (nwritten) {
            sg_buf *sgp = conn->x.out.sgp;
            if ((size_t) nwritten < SG_LEN(sgp)) {
                SG_ADVANCE(sgp, (size_t) nwritten);
                nwritten = 0;
            } else {
                nwritten -= SG_LEN(sgp);
                conn->x.out.sgp++;
                conn->x.out.sg_count--;
                if (conn->x.out.sg_count == 0 && nwritten != 0)
                    /* Wrote more than we wanted to?  */
                    abort();
            }
        }
        if (conn->x.out.sg_count == 0) {
            /* Done writing, switch to reading.  */
            /* Don't call shutdown at this point because
             * some implementations cannot deal with half-closed connections.*/
            FD_CLR(conn->fd, &selstate->wfds);
            /* Q: How do we detect failures to send the remaining data
               to the remote side, since we're in non-blocking mode?
               Will we always get errors on the reading side?  */
            dprint("switching fd %d to READING\n", conn->fd);
            conn->state = READING;
            conn->x.in.bufsizebytes_read = 0;
            conn->x.in.bufsize = 0;
            conn->x.in.buf = 0;
            conn->x.in.pos = 0;
            conn->x.in.n_left = 0;
        }
        return 0;

    case READING:
        if (ssflags & SSF_EXCEPTION) {
            if (conn->x.in.buf) {
                free(conn->x.in.buf);
                conn->x.in.buf = 0;
            }
            goto handle_exception;
        }

        if (conn->x.in.bufsizebytes_read == 4) {
            /* Reading data.  */
            dprint("reading %d bytes of data from fd %d\n",
                   (int) conn->x.in.n_left, conn->fd);
            nread = SOCKET_READ(conn->fd, conn->x.in.pos, conn->x.in.n_left);
            if (nread <= 0) {
                e = nread ? SOCKET_ERRNO : ECONNRESET;
                free(conn->x.in.buf);
                conn->x.in.buf = 0;
                TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, conn, e);
                goto kill_conn;
            }
            conn->x.in.n_left -= nread;
            conn->x.in.pos += nread;
            if (conn->x.in.n_left <= 0) {
                /* We win!  */
                return 1;
            }
        } else {
            /* Reading length.  */
            nread = SOCKET_READ(conn->fd,
                                conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
                                4 - conn->x.in.bufsizebytes_read);
            if (nread < 0) {
                TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, conn, e);
                e = SOCKET_ERRNO;
                goto kill_conn;
            }
            conn->x.in.bufsizebytes_read += nread;
            if (conn->x.in.bufsizebytes_read == 4) {
                unsigned long len = load_32_be (conn->x.in.bufsizebytes);
                dprint("received length on fd %d is %d\n", conn->fd, (int)len);
                /* Arbitrary 1M cap.  */
                if (len > 1 * 1024 * 1024) {
                    e = E2BIG;
                    goto kill_conn;
                }
                conn->x.in.bufsize = conn->x.in.n_left = len;
                conn->x.in.buf = conn->x.in.pos = malloc(len);
                dprint("allocated %d byte buffer at %p\n", (int) len,
                       conn->x.in.buf);
                if (conn->x.in.buf == 0) {
                    /* allocation failure */
                    e = ENOMEM;
                    goto kill_conn;
                }
            }
        }
        break;

    default:
        abort();
    }
    return 0;
}