/* ssl/s2_pkt.c */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 *
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 *
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * 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 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */
/* ====================================================================
 * Copyright (c) 1998-2001 The OpenSSL Project.  All rights reserved.
 *
 * 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.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
 *
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    openssl-core@openssl.org.
 *
 * 5. Products derived from this software may not be called "OpenSSL"
 *    nor may "OpenSSL" appear in their names without prior written
 *    permission of the OpenSSL Project.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
 *
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
 * EXPRESSED 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 OpenSSL PROJECT OR
 * ITS 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.
 * ====================================================================
 *
 * This product includes cryptographic software written by Eric Young
 * (eay@cryptsoft.com).  This product includes software written by Tim
 * Hudson (tjh@cryptsoft.com).
 *
 */

#include "ssl_locl.h"
#ifndef OPENSSL_NO_SSL2
# include <stdio.h>
# include <errno.h>
# define USE_SOCKETS

static int read_n(SSL *s, unsigned int n, unsigned int max,
                  unsigned int extend);
static int n_do_ssl_write(SSL *s, const unsigned char *buf, unsigned int len);
static int write_pending(SSL *s, const unsigned char *buf, unsigned int len);
static int ssl_mt_error(int n);

/*
 * SSL 2.0 imlementation for SSL_read/SSL_peek - This routine will return 0
 * to len bytes, decrypted etc if required.
 */
static int ssl2_read_internal(SSL *s, void *buf, int len, int peek)
{
    int n;
    unsigned char mac[MAX_MAC_SIZE];
    unsigned char *p;
    int i;
    int mac_size;

 ssl2_read_again:
    if (SSL_in_init(s) && !s->in_handshake) {
        n = s->handshake_func(s);
        if (n < 0)
            return (n);
        if (n == 0) {
            SSLerr(SSL_F_SSL2_READ_INTERNAL, SSL_R_SSL_HANDSHAKE_FAILURE);
            return (-1);
        }
    }

    clear_sys_error();
    s->rwstate = SSL_NOTHING;
    if (len <= 0)
        return (len);

    if (s->s2->ract_data_length != 0) { /* read from buffer */
        if (len > s->s2->ract_data_length)
            n = s->s2->ract_data_length;
        else
            n = len;

        memcpy(buf, s->s2->ract_data, (unsigned int)n);
        if (!peek) {
            s->s2->ract_data_length -= n;
            s->s2->ract_data += n;
            if (s->s2->ract_data_length == 0)
                s->rstate = SSL_ST_READ_HEADER;
        }

        return (n);
    }

    /*
     * s->s2->ract_data_length == 0 Fill the buffer, then goto
     * ssl2_read_again.
     */

    if (s->rstate == SSL_ST_READ_HEADER) {
        if (s->first_packet) {
            n = read_n(s, 5, SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER + 2, 0);
            if (n <= 0)
                return (n);     /* error or non-blocking */
            s->first_packet = 0;
            p = s->packet;
            if (!((p[0] & 0x80) && ((p[2] == SSL2_MT_CLIENT_HELLO) ||
                                    (p[2] == SSL2_MT_SERVER_HELLO)))) {
                SSLerr(SSL_F_SSL2_READ_INTERNAL,
                       SSL_R_NON_SSLV2_INITIAL_PACKET);
                return (-1);
            }
        } else {
            n = read_n(s, 2, SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER + 2, 0);
            if (n <= 0)
                return (n);     /* error or non-blocking */
        }
        /* part read stuff */

        s->rstate = SSL_ST_READ_BODY;
        p = s->packet;
        /* Do header */
        /*
         * s->s2->padding=0;
         */
        s->s2->escape = 0;
        s->s2->rlength = (((unsigned int)p[0]) << 8) | ((unsigned int)p[1]);
        if ((p[0] & TWO_BYTE_BIT)) { /* Two byte header? */
            s->s2->three_byte_header = 0;
            s->s2->rlength &= TWO_BYTE_MASK;
        } else {
            s->s2->three_byte_header = 1;
            s->s2->rlength &= THREE_BYTE_MASK;

            /* security >s2->escape */
            s->s2->escape = ((p[0] & SEC_ESC_BIT)) ? 1 : 0;
        }
    }

    if (s->rstate == SSL_ST_READ_BODY) {
        n = s->s2->rlength + 2 + s->s2->three_byte_header;
        if (n > (int)s->packet_length) {
            n -= s->packet_length;
            i = read_n(s, (unsigned int)n, (unsigned int)n, 1);
            if (i <= 0)
                return (i);     /* ERROR */
        }

        p = &(s->packet[2]);
        s->rstate = SSL_ST_READ_HEADER;
        if (s->s2->three_byte_header)
            s->s2->padding = *(p++);
        else
            s->s2->padding = 0;

        /* Data portion */
        if (s->s2->clear_text) {
            mac_size = 0;
            s->s2->mac_data = p;
            s->s2->ract_data = p;
            if (s->s2->padding) {
                SSLerr(SSL_F_SSL2_READ_INTERNAL, SSL_R_ILLEGAL_PADDING);
                return (-1);
            }
        } else {
            mac_size = EVP_MD_CTX_size(s->read_hash);
            if (mac_size < 0)
                return -1;
            OPENSSL_assert(mac_size <= MAX_MAC_SIZE);
            s->s2->mac_data = p;
            s->s2->ract_data = &p[mac_size];
            if (s->s2->padding + mac_size > s->s2->rlength) {
                SSLerr(SSL_F_SSL2_READ_INTERNAL, SSL_R_ILLEGAL_PADDING);
                return (-1);
            }
        }

        s->s2->ract_data_length = s->s2->rlength;
        /*
         * added a check for length > max_size in case encryption was not
         * turned on yet due to an error
         */
        if ((!s->s2->clear_text) &&
            (s->s2->rlength >= (unsigned int)mac_size)) {
            if (!ssl2_enc(s, 0)) {
                SSLerr(SSL_F_SSL2_READ_INTERNAL, SSL_R_DECRYPTION_FAILED);
                return (-1);
            }
            s->s2->ract_data_length -= mac_size;
            ssl2_mac(s, mac, 0);
            s->s2->ract_data_length -= s->s2->padding;
            if ((CRYPTO_memcmp(mac, s->s2->mac_data, mac_size) != 0) ||
                (s->s2->rlength %
                 EVP_CIPHER_CTX_block_size(s->enc_read_ctx) != 0)) {
                SSLerr(SSL_F_SSL2_READ_INTERNAL, SSL_R_BAD_MAC_DECODE);
                return (-1);
            }
        }
        INC32(s->s2->read_sequence); /* expect next number */
        /* s->s2->ract_data is now available for processing */

        /*
         * Possibly the packet that we just read had 0 actual data bytes.
         * (SSLeay/OpenSSL itself never sends such packets; see ssl2_write.)
         * In this case, returning 0 would be interpreted by the caller as
         * indicating EOF, so it's not a good idea.  Instead, we just
         * continue reading; thus ssl2_read_internal may have to process
         * multiple packets before it can return. [Note that using select()
         * for blocking sockets *never* guarantees that the next SSL_read
         * will not block -- the available data may contain incomplete
         * packets, and except for SSL 2, renegotiation can confuse things
         * even more.]
         */

        goto ssl2_read_again;   /* This should really be "return
                                 * ssl2_read(s,buf,len)", but that would
                                 * allow for denial-of-service attacks if a C
                                 * compiler is used that does not recognize
                                 * end-recursion. */
    } else {
        SSLerr(SSL_F_SSL2_READ_INTERNAL, SSL_R_BAD_STATE);
        return (-1);
    }
}

int ssl2_read(SSL *s, void *buf, int len)
{
    return ssl2_read_internal(s, buf, len, 0);
}

int ssl2_peek(SSL *s, void *buf, int len)
{
    return ssl2_read_internal(s, buf, len, 1);
}

/*
 * Return values are as per SSL_read()
 */
static int read_n(SSL *s, unsigned int n, unsigned int max,
                  unsigned int extend)
{
    int i, off, newb;

    /*
     * if there is stuff still in the buffer from a previous read, and there
     * is more than we want, take some.
     */
    if (s->s2->rbuf_left >= (int)n) {
        if (extend)
            s->packet_length += n;
        else {
            s->packet = &(s->s2->rbuf[s->s2->rbuf_offs]);
            s->packet_length = n;
        }
        s->s2->rbuf_left -= n;
        s->s2->rbuf_offs += n;
        return (n);
    }

    if (!s->read_ahead)
        max = n;
    if (max > (unsigned int)(SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER + 2))
        max = SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER + 2;

    /*
     * Else we want more than we have. First, if there is some left or we
     * want to extend
     */
    off = 0;
    if ((s->s2->rbuf_left != 0) || ((s->packet_length != 0) && extend)) {
        newb = s->s2->rbuf_left;
        if (extend) {
            off = s->packet_length;
            if (s->packet != s->s2->rbuf)
                memcpy(s->s2->rbuf, s->packet, (unsigned int)newb + off);
        } else if (s->s2->rbuf_offs != 0) {
            memcpy(s->s2->rbuf, &(s->s2->rbuf[s->s2->rbuf_offs]),
                   (unsigned int)newb);
            s->s2->rbuf_offs = 0;
        }
        s->s2->rbuf_left = 0;
    } else
        newb = 0;

    /*
     * off is the offset to start writing too. r->s2->rbuf_offs is the
     * 'unread data', now 0. newb is the number of new bytes so far
     */
    s->packet = s->s2->rbuf;
    while (newb < (int)n) {
        clear_sys_error();
        if (s->rbio != NULL) {
            s->rwstate = SSL_READING;
            i = BIO_read(s->rbio, (char *)&(s->s2->rbuf[off + newb]),
                         max - newb);
        } else {
            SSLerr(SSL_F_READ_N, SSL_R_READ_BIO_NOT_SET);
            i = -1;
        }
# ifdef PKT_DEBUG
        if (s->debug & 0x01)
            sleep(1);
# endif
        if (i <= 0) {
            s->s2->rbuf_left += newb;
            return i;
        }
        newb += i;
    }

    /* record unread data */
    if (newb > (int)n) {
        s->s2->rbuf_offs = n + off;
        s->s2->rbuf_left = newb - n;
    } else {
        s->s2->rbuf_offs = 0;
        s->s2->rbuf_left = 0;
    }
    if (extend)
        s->packet_length += n;
    else
        s->packet_length = n;
    s->rwstate = SSL_NOTHING;
    return (n);
}

int ssl2_write(SSL *s, const void *_buf, int len)
{
    const unsigned char *buf = _buf;
    unsigned int n, tot;
    int i;

    if (SSL_in_init(s) && !s->in_handshake) {
        i = s->handshake_func(s);
        if (i < 0)
            return (i);
        if (i == 0) {
            SSLerr(SSL_F_SSL2_WRITE, SSL_R_SSL_HANDSHAKE_FAILURE);
            return (-1);
        }
    }

    if (s->error) {
        ssl2_write_error(s);
        if (s->error)
            return (-1);
    }

    clear_sys_error();
    s->rwstate = SSL_NOTHING;
    if (len <= 0)
        return (len);

    tot = s->s2->wnum;
    s->s2->wnum = 0;

    n = (len - tot);
    for (;;) {
        i = n_do_ssl_write(s, &(buf[tot]), n);
        if (i <= 0) {
            s->s2->wnum = tot;
            return (i);
        }
        if ((i == (int)n) || (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)) {
            return (tot + i);
        }

        n -= i;
        tot += i;
    }
}

/*
 * Return values are as per SSL_write()
 */
static int write_pending(SSL *s, const unsigned char *buf, unsigned int len)
{
    int i;

    /* s->s2->wpend_len != 0 MUST be true. */

    /*
     * check that they have given us the same buffer to write
     */
    if ((s->s2->wpend_tot > (int)len) ||
        ((s->s2->wpend_buf != buf) &&
         !(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER))) {
        SSLerr(SSL_F_WRITE_PENDING, SSL_R_BAD_WRITE_RETRY);
        return (-1);
    }

    for (;;) {
        clear_sys_error();
        if (s->wbio != NULL) {
            s->rwstate = SSL_WRITING;
            i = BIO_write(s->wbio,
                          (char *)&(s->s2->write_ptr[s->s2->wpend_off]),
                          (unsigned int)s->s2->wpend_len);
        } else {
            SSLerr(SSL_F_WRITE_PENDING, SSL_R_WRITE_BIO_NOT_SET);
            i = -1;
        }
# ifdef PKT_DEBUG
        if (s->debug & 0x01)
            sleep(1);
# endif
        if (i == s->s2->wpend_len) {
            s->s2->wpend_len = 0;
            s->rwstate = SSL_NOTHING;
            return (s->s2->wpend_ret);
        } else if (i <= 0)
            return i;
        s->s2->wpend_off += i;
        s->s2->wpend_len -= i;
    }
}

static int n_do_ssl_write(SSL *s, const unsigned char *buf, unsigned int len)
{
    unsigned int j, k, olen, p, bs;
    int mac_size;
    register unsigned char *pp;

    olen = len;

    /*
     * first check if there is data from an encryption waiting to be sent -
     * it must be sent because the other end is waiting. This will happen
     * with non-blocking IO.  We print it and then return.
     */
    if (s->s2->wpend_len != 0)
        return (write_pending(s, buf, len));

    /* set mac_size to mac size */
    if (s->s2->clear_text)
        mac_size = 0;
    else {
        mac_size = EVP_MD_CTX_size(s->write_hash);
        if (mac_size < 0)
            return -1;
    }

    /* lets set the pad p */
    if (s->s2->clear_text) {
        if (len > SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER)
            len = SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER;
        p = 0;
        s->s2->three_byte_header = 0;
        /* len=len; */
    } else {
        bs = EVP_CIPHER_CTX_block_size(s->enc_read_ctx);
        j = len + mac_size;
        /*
         * Two-byte headers allow for a larger record length than three-byte
         * headers, but we can't use them if we need padding or if we have to
         * set the escape bit.
         */
        if ((j > SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER) && (!s->s2->escape)) {
            if (j > SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER)
                j = SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER;
            /*
             * set k to the max number of bytes with 2 byte header
             */
            k = j - (j % bs);
            /* how many data bytes? */
            len = k - mac_size;
            s->s2->three_byte_header = 0;
            p = 0;
        } else if ((bs <= 1) && (!s->s2->escape)) {
            /*-
             * j <= SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER, thus
             * j < SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER
             */
            s->s2->three_byte_header = 0;
            p = 0;
        } else {                /* we may have to use a 3 byte header */

            /*-
             * If s->s2->escape is not set, then
             * j <= SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER, and thus
             * j < SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER.
             */
            p = (j % bs);
            p = (p == 0) ? 0 : (bs - p);
            if (s->s2->escape) {
                s->s2->three_byte_header = 1;
                if (j > SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER)
                    j = SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER;
            } else
                s->s2->three_byte_header = (p == 0) ? 0 : 1;
        }
    }

    /*-
     * Now
     *      j <= SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER
     * holds, and if s->s2->three_byte_header is set, then even
     *      j <= SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER.
     */

    /*
     * mac_size is the number of MAC bytes len is the number of data bytes we
     * are going to send p is the number of padding bytes (if it is a
     * two-byte header, then p == 0)
     */

    s->s2->wlength = len;
    s->s2->padding = p;
    s->s2->mac_data = &(s->s2->wbuf[3]);
    s->s2->wact_data = &(s->s2->wbuf[3 + mac_size]);

    /*
     * It would be clearer to write this as follows:
     *     if (mac_size + len + p > SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER)
     * However |len| is user input that could in theory be very large. We
     * know |mac_size| and |p| are small, so to avoid any possibility of
     * overflow we write it like this.
     *
     * In theory this should never fail because the logic above should have
     * modified |len| if it is too big. But we are being cautious.
     */
    if (len > (SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER - (mac_size + p))) {
        return -1;
    }
    /* we copy the data into s->s2->wbuf */
    memcpy(s->s2->wact_data, buf, len);
    if (p)
        memset(&(s->s2->wact_data[len]), 0, p); /* arbitrary padding */

    if (!s->s2->clear_text) {
        s->s2->wact_data_length = len + p;
        ssl2_mac(s, s->s2->mac_data, 1);
        s->s2->wlength += p + mac_size;
        if (ssl2_enc(s, 1) < 1)
            return -1;
    }

    /* package up the header */
    s->s2->wpend_len = s->s2->wlength;
    if (s->s2->three_byte_header) { /* 3 byte header */
        pp = s->s2->mac_data;
        pp -= 3;
        pp[0] = (s->s2->wlength >> 8) & (THREE_BYTE_MASK >> 8);
        if (s->s2->escape)
            pp[0] |= SEC_ESC_BIT;
        pp[1] = s->s2->wlength & 0xff;
        pp[2] = s->s2->padding;
        s->s2->wpend_len += 3;
    } else {
        pp = s->s2->mac_data;
        pp -= 2;
        pp[0] = ((s->s2->wlength >> 8) & (TWO_BYTE_MASK >> 8)) | TWO_BYTE_BIT;
        pp[1] = s->s2->wlength & 0xff;
        s->s2->wpend_len += 2;
    }
    s->s2->write_ptr = pp;

    INC32(s->s2->write_sequence); /* expect next number */

    /* lets try to actually write the data */
    s->s2->wpend_tot = olen;
    s->s2->wpend_buf = buf;

    s->s2->wpend_ret = len;

    s->s2->wpend_off = 0;
    return (write_pending(s, buf, olen));
}

int ssl2_part_read(SSL *s, unsigned long f, int i)
{
    unsigned char *p;
    int j;

    if (i < 0) {
        /* ssl2_return_error(s); */
        /*
         * for non-blocking io, this is not necessarily fatal
         */
        return (i);
    } else {
        s->init_num += i;

        /*
         * Check for error.  While there are recoverable errors, this
         * function is not called when those must be expected; any error
         * detected here is fatal.
         */
        if (s->init_num >= 3) {
            p = (unsigned char *)s->init_buf->data;
            if (p[0] == SSL2_MT_ERROR) {
                j = (p[1] << 8) | p[2];
                SSLerr((int)f, ssl_mt_error(j));
                s->init_num -= 3;
                if (s->init_num > 0)
                    memmove(p, p + 3, s->init_num);
            }
        }

        /*
         * If it's not an error message, we have some error anyway -- the
         * message was shorter than expected.  This too is treated as fatal
         * (at least if SSL_get_error is asked for its opinion).
         */
        return (0);
    }
}

int ssl2_do_write(SSL *s)
{
    int ret;

    ret = ssl2_write(s, &s->init_buf->data[s->init_off], s->init_num);
    if (ret == s->init_num) {
        if (s->msg_callback)
            s->msg_callback(1, s->version, 0, s->init_buf->data,
                            (size_t)(s->init_off + s->init_num), s,
                            s->msg_callback_arg);
        return (1);
    }
    if (ret < 0)
        return (-1);
    s->init_off += ret;
    s->init_num -= ret;
    return (0);
}

static int ssl_mt_error(int n)
{
    int ret;

    switch (n) {
    case SSL2_PE_NO_CIPHER:
        ret = SSL_R_PEER_ERROR_NO_CIPHER;
        break;
    case SSL2_PE_NO_CERTIFICATE:
        ret = SSL_R_PEER_ERROR_NO_CERTIFICATE;
        break;
    case SSL2_PE_BAD_CERTIFICATE:
        ret = SSL_R_PEER_ERROR_CERTIFICATE;
        break;
    case SSL2_PE_UNSUPPORTED_CERTIFICATE_TYPE:
        ret = SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE;
        break;
    default:
        ret = SSL_R_UNKNOWN_REMOTE_ERROR_TYPE;
        break;
    }
    return (ret);
}
#else                           /* !OPENSSL_NO_SSL2 */

# if PEDANTIC
static void *dummy = &dummy;
# endif

#endif
