Пример #1
0
uint8_t SOCK_Socket(uint8_t sock, uint8_t eth_protocol, uint16_t tcp_port) {
  uint8_t val, retval = 0;

  if (sock!=0) {
    return retval;
  }
  /* Make sure we close the socket first */
  if (W5100_MemReadByte(W5100_S0_SR, &val)!=ERR_OK) {
    return 0; /* failure */
  }
  if (val==W5100_SOCK_CLOSED) {
    SOCK_Close(sock);
  }
  /* Assigned Socket 0 Mode Register */
  W5100_MemWriteByte(W5100_S0_MR, eth_protocol);
  /* Now open the Socket 0 */
  W5100_MemWriteWord(W5100_S0_PORT, tcp_port);
  W5100_MemWriteByte(W5100_S0_CR, W5100_CR_OPEN);  /* Open Socket */
  /* Wait for Opening Process */
  do {
    W5100_MemReadByte(W5100_S0_CR, &val);
  } while(val!=0);
  /* Check for Init Status */
  W5100_MemReadByte(W5100_S0_SR, &val);
  if (val==W5100_SOCK_INIT) {
    retval = 1;
  } else {
    SOCK_Close(sock);
  }
  return retval;
}
EIO_Status CListeningSocket::Accept(CSocket*&       sock,
                                    const STimeout* timeout,
                                    TSOCK_Flags     flags) const
{
    if ( !m_Socket ) {
        sock = 0;
        return eIO_Closed;
    }

    SOCK       x_sock;
    EIO_Status status;
    status = LSOCK_AcceptEx(m_Socket, timeout, &x_sock, flags);
    assert(!x_sock ^ !(status != eIO_Success));
    if (status == eIO_Success) {
        try {
            sock = new CSocket;
        } catch (...) {
            sock = 0;
            SOCK_Abort(x_sock);
            SOCK_Close(x_sock);
            throw;
        }
        sock->Reset(x_sock, eTakeOwnership, eCopyTimeoutsToSOCK);
    } else
        sock = 0;
    return status;
}
void CSocket::Reset(SOCK sock, EOwnership if_to_own, ECopyTimeout whence)
{
    if (m_Socket  &&  m_IsOwned != eNoOwnership)
        SOCK_Close(m_Socket);
    m_Socket  = sock;
    m_IsOwned = if_to_own;
    if (whence == eCopyTimeoutsFromSOCK) {
        if ( sock ) {
            const STimeout* timeout;
            timeout = SOCK_GetTimeout(sock, eIO_Read);
            if ( timeout ) {
                rr_timeout = *timeout;
                r_timeout  = &rr_timeout;
            } else
                r_timeout  = 0;
            timeout = SOCK_GetTimeout(sock, eIO_Write);
            if ( timeout ) {
                ww_timeout = *timeout;
                w_timeout  = &ww_timeout;
            } else
                w_timeout  = 0;
            timeout = SOCK_GetTimeout(sock, eIO_Close);
            if ( timeout ) {
                cc_timeout = *timeout;
                c_timeout  = &cc_timeout;
            } else
                c_timeout  = 0;
        } else
            r_timeout = w_timeout = c_timeout = 0;
    } else if ( sock ) {
        SOCK_SetTimeout(sock, eIO_Read,  r_timeout);
        SOCK_SetTimeout(sock, eIO_Write, w_timeout);
        SOCK_SetTimeout(sock, eIO_Close, c_timeout);
    }
}
EIO_Status CUNIXSocket::Connect(const string&   path,
                                const STimeout* timeout,
                                TSOCK_Flags     flags)
{
    if ( m_Socket ) {
        if (SOCK_Status(m_Socket, eIO_Open) != eIO_Closed)
            return eIO_Unknown;
        if (m_IsOwned != eNoOwnership)
            SOCK_Close(m_Socket);
    }
    if (timeout != kDefaultTimeout) {
        if ( timeout ) {
            if (&oo_timeout != timeout)
                oo_timeout = *timeout;
            o_timeout = &oo_timeout;
        } else
            o_timeout = 0;
    }
    EIO_Status status = SOCK_CreateUNIX(path.c_str(), o_timeout,
                                        &m_Socket, 0, 0, flags);
    if (status == eIO_Success) {
        SOCK_SetTimeout(m_Socket, eIO_Read,  r_timeout);
        SOCK_SetTimeout(m_Socket, eIO_Write, w_timeout);
        SOCK_SetTimeout(m_Socket, eIO_Close, c_timeout);        
    } else
        assert(!m_Socket);
    return status;
}
Пример #5
0
static Int2 s_ServiceDisconnect(NI_HandPtr mhp)
{
  s_EndServices(mhp->disp);
  SOCK_Close((SOCK)mhp->extra_proc_info);
  AsnIoClose(mhp->raip);
  AsnIoClose(mhp->waip);
  MemFree(mhp->hostname);
  MemFree(mhp);
  return 0;
} 
Пример #6
0
/* Unconditionally drop the connection; timeout may specify time allowance */
static void s_DropConnection(SHttpConnector* uuu, const STimeout* timeout)
{
    size_t http_size = BUF_Size(uuu->http);
    assert(uuu->sock);
    if (http_size  &&  BUF_Read(uuu->http, 0, http_size) != http_size) {
        CORE_LOG_X(4, eLOG_Error, "[HTTP]  Cannot discard HTTP header buffer");
        assert(0);
    }
    SOCK_SetTimeout(uuu->sock, eIO_Close, timeout);
    SOCK_Close(uuu->sock);
    uuu->sock = 0;
}
Пример #7
0
static EIO_Status s_VT_Close
(CONNECTOR       connector,
 const STimeout* timeout)
{
    SSockConnector* xxx = (SSockConnector*) connector->handle;
    EIO_Status status = eIO_Success;
    assert(xxx->sock);
    if (xxx->try_own) {
        SOCK_SetTimeout(xxx->sock, eIO_Close, timeout);
        status = SOCK_Close(xxx->sock);
    }
    xxx->sock = 0;
    return status;
}
Пример #8
0
/* Skeletons for the socket i/o test:
 *   TEST__client(...)
 *   TEST__server(...)
 *   establish and close connection;  call test i/o functions like
 *     TEST__[client|server]_[1|2|...] (...)
 */
static void TEST__client(const char*     server_host,
                         unsigned short  server_port,
                         const STimeout* timeout)
{
    SOCK       sock;
    EIO_Status status;
    char       tmo[80];

    if ( timeout )
        sprintf(tmo, "%u.%06u", timeout->sec, timeout->usec);
    else
        strcpy(tmo, "INFINITE");
    CORE_LOGF(eLOG_Note,
              ("TEST__client(host = \"%s\", port = %hu, timeout = %s",
               server_host, server_port, tmo));

    /* Connect to server */
    status = SOCK_Create(server_host, server_port, timeout, &sock);
    assert(status == eIO_Success);
    verify(SOCK_SetTimeout(sock, eIO_ReadWrite, timeout) == eIO_Success);
    verify(SOCK_SetTimeout(sock, eIO_Close,     timeout) == eIO_Success);

    /* Test the simplest randezvous(plain request-reply)
     * The two peer functions are:
     *      "TEST__[client|server]_1(SOCK sock)"
     */
    TEST__client_1(sock);

    /* Test a more complex case
     * The two peer functions are:
     *      "TEST__[client|server]_2(SOCK sock)"
     */
    TEST__client_2(sock);

    /* Close connection and exit */
    status = SOCK_Close(sock);
    assert(status == eIO_Success  ||  status == eIO_Closed);

    CORE_LOG(eLOG_Note, "TEST completed successfully");
}
Пример #9
0
uint8_t SOCK_Listen(uint8_t sock) {
  uint8_t val, retval = 0;
  
  if (sock!=0) {
    return retval;
  }
  W5100_MemReadByte(W5100_S0_SR, &val);
  if (val==W5100_SOCK_INIT) {
    /* Send the LISTEN Command */
    W5100_MemWriteByte(W5100_S0_CR, W5100_CR_LISTEN);
    /* Wait for Listening Process */
    do {
      W5100_MemReadByte(W5100_S0_CR, &val);
    } while(val!=0);
    /* Check for Listen Status */
    W5100_MemReadByte(W5100_S0_SR, &val);
    if (val==W5100_SOCK_LISTEN) {
      retval = 1;
    } else {
      SOCK_Close(sock);
    }
  }
  return retval;
}
Пример #10
0
CSocket::~CSocket()
{
    if (m_Socket  &&  m_IsOwned != eNoOwnership)
        SOCK_Close(m_Socket);
}
Пример #11
0
static CConn_IOStream::TConn_Pair
s_SocketConnectorBuilder(const SConnNetInfo* net_info,
                         const STimeout*     timeout,
                         const void*         data,
                         size_t              size,
                         TSOCK_Flags         flags)
{
    EIO_Status status = eIO_Success;
    bool       proxy = false;
    SOCK       sock = 0;

    _ASSERT(net_info);
    if ((flags & (fSOCK_LogOn | fSOCK_LogDefault)) == fSOCK_LogDefault
        &&  net_info->debug_printout == eDebugPrintout_Data) {
        flags &= ~fSOCK_LogDefault;
        flags |=  fSOCK_LogOn;
    }
    if (*net_info->http_proxy_host  &&  net_info->http_proxy_port) {
        status = HTTP_CreateTunnel(net_info, fHTTP_NoAutoRetry, &sock);
        _ASSERT(!sock ^ !(status != eIO_Success));
        if (status == eIO_Success
            &&  ((flags & ~(fSOCK_LogOn | fSOCK_LogDefault))  ||  size)) {
            SOCK s;
            status = SOCK_CreateOnTopEx(sock, 0, &s,
                                        data, size, flags);
            _ASSERT(!s ^ !(status != eIO_Success));
            SOCK_Destroy(sock);
            sock = s;
        }
        proxy = true;
    }
    if (!sock  &&  (!proxy  ||  net_info->http_proxy_leak)) {
        const char* host = (net_info->firewall  &&  *net_info->proxy_host
                            ? net_info->proxy_host : net_info->host);
        if (timeout == kDefaultTimeout)
            timeout  = net_info->timeout;
        if (!proxy  &&  net_info->debug_printout) {
            SConnNetInfo* x_net_info = ConnNetInfo_Clone(net_info);
            if (x_net_info) {
                x_net_info->req_method = eReqMethod_Any;
                x_net_info->stateless = 0;
                x_net_info->lb_disable = 0;
                x_net_info->http_proxy_host[0] = '\0';
                x_net_info->http_proxy_port    =   0;
                x_net_info->http_proxy_user[0] = '\0';
                x_net_info->http_proxy_pass[0] = '\0';
                x_net_info->proxy_host[0]      = '\0';
                ConnNetInfo_SetUserHeader(x_net_info, 0);
                if (x_net_info->http_referer) {
                    free((void*) x_net_info->http_referer);
                    x_net_info->http_referer = 0;
                }
                x_net_info->timeout = timeout;
            }
            ConnNetInfo_Log(x_net_info, eLOG_Note, CORE_GetLOG());
            ConnNetInfo_Destroy(x_net_info);
        }
        status = SOCK_CreateEx(host, net_info->port, timeout, &sock,
                               data, size, flags);
        _ASSERT(!sock ^ !(status != eIO_Success));
    }
    string hostport(net_info->host);
    hostport += ':';
    hostport += NStr::UIntToString(net_info->port);
    CONNECTOR c = SOCK_CreateConnectorOnTopEx(sock,
                                              1/*own*/,
                                              hostport.c_str());
    if (!c) {
        SOCK_Abort(sock);
        SOCK_Close(sock);
        status = eIO_Unknown;
    }
    return CConn_IOStream::TConn_Pair(c, status);
}
Пример #12
0
static void TEST__server_2(SOCK sock, LSOCK lsock)
{
    EIO_Status status;
    size_t     n_io, n_io_done;
    char       buf[TEST_BUFSIZE];
    STimeout   r_to, w_to, rc_to;
    size_t     i;

    CORE_LOG(eLOG_Note, "TEST__server_2(TS2)");

    r_to.sec   = 0;
    r_to.usec  = 0;
    w_to = r_to;

    rc_to.sec  = 30;
    rc_to.usec = 123456;

    /* goto */
 l_reconnect: /* reconnection loopback */
    SOCK_SetDataLogging(sock, eOn);

    status = SOCK_SetTimeout(sock, eIO_Read,  &r_to);
    assert(status == eIO_Success);
    status = SOCK_SetTimeout(sock, eIO_Write, &w_to);
    assert(status == eIO_Success);

    for (i = 0;  ;  i++) {
        char* x_buf;

        /* read data from socket */
        n_io = sizeof(buf);
        status = SOCK_Read(sock, buf, n_io, &n_io_done, eIO_ReadPlain);
        switch ( status ) {
        case eIO_Success:
            CORE_LOGF(eLOG_Note,
                      ("TS2::read:"
                       " [%lu], status=%7s, n_io=%5lu, n_io_done=%5lu",
                       (unsigned long)i, IO_StatusStr(status),
                       (unsigned long)n_io, (unsigned long)n_io_done));
            assert(n_io_done > 0);
            break;

        case eIO_Closed:
            CORE_LOG(eLOG_Note, "TS2::read: connection closed");
            assert(SOCK_Status(sock, eIO_Read) == eIO_Closed);
            /* close connection */
            status = SOCK_Close(sock);
            assert(status == eIO_Success  ||  status == eIO_Closed);
            /* reconnect */
            if ( !lsock )
                return;

            CORE_LOG(eLOG_Note, "TS2::reconnect");
            if ((status = LSOCK_Accept(lsock, &rc_to, &sock)) != eIO_Success)
                return;
            assert(SOCK_Status(sock, eIO_Read) == eIO_Success);
            /* !!! */
            goto l_reconnect;

        case eIO_Timeout:
            CORE_LOGF(eLOG_Note,
                      ("TS2::read:"
                       " [%lu] timeout expired: %5u sec, %6u usec",
                       (unsigned long)i, r_to.sec, r_to.usec));
            assert(n_io_done == 0);
            s_DoubleTimeout(&r_to);
            status = SOCK_SetTimeout(sock, eIO_Read, &r_to);
            assert(status == eIO_Success);
            assert(SOCK_Status(sock, eIO_Read) == eIO_Timeout);
            break;

        default:
            CORE_LOGF(eLOG_Fatal,
                      ("TS2::read: status = %d", (int) status));
        } /* switch */

        /* write(just the same) data back to client */
        n_io  = n_io_done;
        x_buf = buf;
        while ( n_io ) {
            status = SOCK_Write(sock, buf, n_io, &n_io_done, eIO_WritePersist);
            switch ( status ) {
            case eIO_Success:
                CORE_LOGF(eLOG_Note,
                          ("TS2::write:"
                           " [%lu], status=%7s, n_io=%5lu, n_io_done=%5lu",
                           (unsigned long)i, IO_StatusStr(status),
                           (unsigned long)n_io, (unsigned long)n_io_done));
                assert(n_io_done > 0);
                break;
            case eIO_Closed:
                CORE_LOG(eLOG_Fatal, "TS2::write: connection closed");
                return;
            case eIO_Timeout:
                CORE_LOGF(eLOG_Note,
                          ("TS2::write:"
                           " [%lu] timeout expired: %5u sec, %6u usec",
                           (unsigned long)i, w_to.sec, w_to.usec));
                assert(n_io_done == 0);
                s_DoubleTimeout(&w_to);
                status = SOCK_SetTimeout(sock, eIO_Write, &w_to);
                assert(status == eIO_Success);
                break;
            default:
                CORE_LOGF(eLOG_Fatal,
                          ("TS2::write: status = %d", (int) status));
            } /* switch */

            n_io  -= n_io_done;
            x_buf += n_io_done;
        }
    }
}
Пример #13
0
static void TEST__server_1(SOCK sock)
{
    EIO_Status status;
    size_t     n_io, n_io_done;
    char       buf[TEST_BUFSIZE];

    CORE_LOG(eLOG_Note, "TEST__server_1(TS1)");

    /* Receive and send back a short string */
    SOCK_SetDataLogging(sock, eOn);
    n_io = strlen(s_C1) + 1;
    status = SOCK_Read(sock, buf, n_io, &n_io_done, eIO_ReadPlain);
    assert(status == eIO_Success  &&  n_io == n_io_done);
    assert(strcmp(buf, s_C1) == 0  ||  strcmp(buf, s_M1) == 0);

    SOCK_SetDataLogging(sock, eDefault);
    SOCK_SetDataLoggingAPI(eOn);
    n_io = strlen(s_S1) + 1;
    status = SOCK_Write(sock, s_S1, n_io, &n_io_done, eIO_WritePersist);
    assert(status == eIO_Success  &&  n_io == n_io_done);
    SOCK_SetDataLoggingAPI(eOff);

    /* Receive a very big binary blob, and check its content */
    {{
#define DO_LOG_SIZE    300
#define DONT_LOG_SIZE  BIG_BLOB_SIZE - DO_LOG_SIZE
        unsigned char* blob = (unsigned char*) malloc(BIG_BLOB_SIZE);

        status = SOCK_Read(sock,blob,DONT_LOG_SIZE,&n_io_done,eIO_ReadPersist);
        assert(status == eIO_Success  &&  n_io_done == DONT_LOG_SIZE);

        SOCK_SetDataLogging(sock, eOn);
        status = SOCK_Read(sock, blob + DONT_LOG_SIZE, DO_LOG_SIZE,
                           &n_io_done, eIO_ReadPersist);
        assert(status == eIO_Success  &&  n_io_done == DO_LOG_SIZE);
        SOCK_SetDataLogging(sock, eDefault);

        for (n_io = 0;  n_io < BIG_BLOB_SIZE;  n_io++)
            assert(blob[n_io] == (unsigned char) n_io);
        free(blob);
    }}

    /* Receive a very big binary blob, and write data back */
    {{
        unsigned char* blob = (unsigned char*) malloc(BIG_BLOB_SIZE);
        int i;
        for (i = 0;  i < 10;  i++) {
            /*            X_SLEEP(1);*/
            status = SOCK_Read(sock, blob + i * SUB_BLOB_SIZE, SUB_BLOB_SIZE,
                               &n_io_done, eIO_ReadPersist);
            assert(status == eIO_Success  &&  n_io_done == SUB_BLOB_SIZE);
            status = SOCK_Write(sock, blob + i * SUB_BLOB_SIZE, SUB_BLOB_SIZE,
                                &n_io_done, eIO_WritePersist);
            assert(status == eIO_Success  &&  n_io_done == SUB_BLOB_SIZE);
        }
        for (n_io = 0;  n_io < BIG_BLOB_SIZE;  n_io++)
            assert(blob[n_io] == (unsigned char) n_io);
        free(blob);
    }}

    /* Shutdown on write */
#ifdef NCBI_OS_MSWIN
    assert(SOCK_Shutdown(sock, eIO_ReadWrite) == eIO_Success);
#else
    assert(SOCK_Shutdown(sock, eIO_Write)     == eIO_Success);
#endif
    assert(SOCK_Status  (sock, eIO_Write)     == eIO_Closed);
    assert(SOCK_Write   (sock, 0, 0, &n_io_done, eIO_WritePersist)
                                              == eIO_Closed);
    assert(SOCK_Status  (sock, eIO_Write)     == eIO_Closed);
#ifdef NCBI_OS_MSWIN
    assert(SOCK_Status  (sock, eIO_Read)      == eIO_Closed);
#else
    assert(SOCK_Status  (sock, eIO_Read)      == eIO_Success);
#endif
    assert(SOCK_Close   (sock)                == eIO_Success);
}
Пример #14
0
int main(int argc, char** argv)
{
    /* Prepare to connect:  parse and check cmd.-line args, etc. */
    const char*    host        = argc > 1 ? argv[1]       : "";
    unsigned short port        = argc > 2 ? atoi(argv[2]) : CONN_PORT_HTTP;
    const char*    path        = argc > 3 ? argv[3]       : "";
    const char*    args        = argc > 4 ? argv[4]       : "";
    const char*    inp_file    = argc > 5 ? argv[5]       : "";
    const char*    user_header = argc > 6 ? argv[6]       : "";

    size_t   content_length;
    STimeout timeout;

    SOCK sock;
    EIO_Status status;
    char buffer[10000];

    CORE_SetLOGFormatFlags(fLOG_None          | fLOG_Level   |
                           fLOG_OmitNoteLevel | fLOG_DateTime);
    CORE_SetLOGFILE(stderr, 0/*false*/);

    SOCK_SetupSSL(NcbiSetupTls);

    fprintf(stderr, "Running...\n"
            "  Executable:      '%s'\n"
            "  URL host:        '%s'\n"
            "  URL port:         %hu\n"
            "  URL path:        '%s'\n"
            "  URL args:        '%s'\n"
            "  Input data file: '%s'\n"
            "  User header:     '%s'\n"
            "Response(if any) from the hit URL goes to standard output.\n\n",
            argv[0], host, port, path, args, inp_file, user_header);

    if ( argc < 4 ) {
        fprintf(stderr,
                "Usage:   %s host port path args inp_file [user_header]\n",
                argv[0]);
        CORE_LOG(eLOG_Fatal, "Two few arguments");
        return 1;
    }

    {{
        FILE *fp = fopen(inp_file, "rb");
        long offset;

        if ( !fp ) {
            CORE_LOGF(eLOG_Fatal, ("Non-existent file '%s'", inp_file));
            return 2;
        }
        if ( fseek(fp, 0, SEEK_END) != 0  ||  (offset = ftell(fp)) < 0 ) {
            CORE_LOGF(eLOG_Fatal,
                      ("Cannot obtain size of file '%s'", inp_file));
            return 2;
        }
        fclose(fp);
        content_length = (size_t) offset;
    }}

    timeout.sec  = 10;
    timeout.usec = 0;
    
    /* Connect */
    sock = URL_Connect(host, port, path, args, /*NCBI_FAKE_WARNING*/
                       eReqMethod_Any, content_length,
                       &timeout, &timeout, user_header, 1/*true*/,
                       port == CONN_PORT_HTTPS
                       ? fSOCK_LogDefault | fSOCK_Secure
                       : fSOCK_LogDefault);
    if ( !sock )
        return 3;

    {{ /* Pump data from the input file to socket */
        FILE* fp = fopen(inp_file, "rb");
        if ( !fp ) {
            CORE_LOGF(eLOG_Fatal, 
                      ("Cannot open file '%s' for reading", inp_file));
            return 4;
        }

        for (;;) {
            size_t n_written;
            size_t n_read = fread(buffer, 1, sizeof(buffer), fp);
            if ( n_read <= 0 ) {
                if ( content_length ) {
                    CORE_LOGF(eLOG_Fatal,
                              ("Cannot read last %lu bytes from file '%s'",
                               (unsigned long) content_length, inp_file));
                    return 5;
                }
                break;
            }

            assert(content_length >= n_read);
            content_length -= n_read;
            status = SOCK_Write(sock, buffer, n_read,
                                &n_written, eIO_WritePersist);
            if ( status != eIO_Success ) {
                CORE_LOGF(eLOG_Fatal,
                          ("Error writing to socket: %s",
                           IO_StatusStr(status)));
                return 6;
            }
        }

        fclose(fp);
    }}

    /* Read reply from socket, write it to STDOUT */
    {{
        size_t n_read;
        for (;;) {
            status = SOCK_Read(sock, buffer, sizeof(buffer), &n_read,
                               eIO_ReadPlain);
            if (status != eIO_Success)
                break;

            fwrite(buffer, 1, n_read, stdout);
        }

        if ( status != eIO_Closed ) {
            CORE_LOGF(eLOG_Error,
                      ("Read error after %ld byte(s) from socket: %s",
                       (long) content_length, IO_StatusStr(status)));
        }
        fprintf(stdout, "\n");
    }}

    /* Success:  close the socket, cleanup, and exit */
    SOCK_Close(sock);

    CORE_LOG(eLOG_Note, "TEST completed successfully");
    CORE_SetLOG(0);
    return 0;
}
Пример #15
0
const char* CORE_SendMailEx(const char*          to,
                            const char*          subject,
                            const char*          body,
                            const SSendMailInfo* uinfo)
{
    static const STimeout zero = {0, 0};
    const SSendMailInfo* info;
    SSendMailInfo ainfo;
    char buffer[1024];
    SOCK sock = 0;

    info = uinfo ? uinfo : SendMailInfo_Init(&ainfo);
    if (info->magic_number != MX_MAGIC_NUMBER)
        SENDMAIL_RETURN(6, "Invalid magic number");

    if ((!to         ||  !*to)        &&
        (!info->cc   ||  !*info->cc)  &&
        (!info->bcc  ||  !*info->bcc)) {
        SENDMAIL_RETURN(7, "At least one message recipient must be specified");
    }

    /* Open connection to sendmail */
    if (SOCK_Create(info->mx_host, info->mx_port, &info->mx_timeout, &sock)
        != eIO_Success) {
        SENDMAIL_RETURN(8, "Cannot connect to sendmail");
    }
    SOCK_SetTimeout(sock, eIO_ReadWrite, &info->mx_timeout);

    /* Follow the protocol conversation, RFC821 */
    if (!SENDMAIL_READ_RESPONSE(220, 0, buffer))
        SENDMAIL_RETURN2(9, "Protocol error in connection init", buffer);

    if ((!(info->mx_options & fSendMail_StripNonFQDNHost)  ||
         !SOCK_gethostbyaddr(0, buffer, sizeof(buffer)))  &&
        SOCK_gethostname(buffer, sizeof(buffer)) != 0) {
        SENDMAIL_RETURN(10, "Unable to get local host name");
    }
    if (!s_SockWrite(sock, "HELO ", 0)  ||
        !s_SockWrite(sock, buffer, 0)   ||
        !s_SockWrite(sock, MX_CRLF, 2)) {
        SENDMAIL_RETURN(11, "Write error in HELO command");
    }
    if (!SENDMAIL_READ_RESPONSE(250, 0, buffer))
        SENDMAIL_RETURN2(12, "Protocol error in HELO command", buffer);

    if (!s_SockWrite(sock, "MAIL FROM: <", 0)             ||
        !s_SockWrite(sock, info->from, s_FromSize(info))  ||
        !s_SockWrite(sock, ">" MX_CRLF, 1 + 2)) {
        SENDMAIL_RETURN(13, "Write error in MAIL command");
    }
    if (!SENDMAIL_READ_RESPONSE(250, 0, buffer))
        SENDMAIL_RETURN2(14, "Protocol error in MAIL command", buffer);

    if (to && *to) {
        const char* error = SENDMAIL_SENDRCPT("To", to, buffer);
        if (error)
            return error;
    }

    if (info->cc && *info->cc) {
        const char* error = SENDMAIL_SENDRCPT("Cc", info->cc, buffer);
        if (error)
            return error;
    }

    if (info->bcc && *info->bcc) {
        const char* error = SENDMAIL_SENDRCPT("Bcc", info->bcc, buffer);
        if (error)
            return error;
    }

    if (!s_SockWrite(sock, "DATA" MX_CRLF, 0))
        SENDMAIL_RETURN(15, "Write error in DATA command");
    if (!SENDMAIL_READ_RESPONSE(354, 0, buffer))
        SENDMAIL_RETURN2(16, "Protocol error in DATA command", buffer);

    if (!(info->mx_options & fSendMail_NoMxHeader)) {
        /* Follow RFC822 to compose message headers. Note that
         * 'Date:'and 'From:' are both added by sendmail automagically.
         */ 
        if (!s_SockWrite(sock, "Subject: ", 0)             ||
            (subject  &&  !s_SockWrite(sock, subject, 0))  ||
            !s_SockWrite(sock, MX_CRLF, 2))
            SENDMAIL_RETURN(17, "Write error in sending subject");

        if (to  &&  *to) {
            if (!s_SockWrite(sock, "To: ", 0)              ||
                !s_SockWrite(sock, to, 0)                  ||
                !s_SockWrite(sock, MX_CRLF, 2))
                SENDMAIL_RETURN(18, "Write error in sending To");
        }

        if (info->cc  &&  *info->cc) {
            if (!s_SockWrite(sock, "Cc: ", 0)              ||
                !s_SockWrite(sock, info->cc, 0)            ||
                !s_SockWrite(sock, MX_CRLF, 2))
                SENDMAIL_RETURN(19, "Write error in sending Cc");
        }
    } else if (subject && *subject)
        CORE_LOG_X(2, eLOG_Warning,
                   "[SendMail]  Subject ignored in as-is messages");

    if (!s_SockWrite(sock, "X-Mailer: CORE_SendMail (NCBI "
                     NCBI_SENDMAIL_TOOLKIT " Toolkit)" MX_CRLF, 0)) {
        SENDMAIL_RETURN(20, "Write error in sending mailer information");
    }

    assert(sizeof(buffer) > sizeof(MX_CRLF) && sizeof(MX_CRLF) >= 3);

    if (info->header && *info->header) {
        size_t n = 0, m = strlen(info->header);
        int/*bool*/ newline = 0/*false*/;
        while (n < m) {
            size_t k = 0;
            if (SOCK_Wait(sock, eIO_Read, &zero) != eIO_Timeout)
                break;
            while (k < sizeof(buffer) - sizeof(MX_CRLF)) {
                if (info->header[n] == '\n') {
                    memcpy(&buffer[k], MX_CRLF, sizeof(MX_CRLF) - 1);
                    k += sizeof(MX_CRLF) - 1;
                    newline = 1/*true*/;
                } else {
                    if (info->header[n] != '\r'  ||  info->header[n+1] != '\n')
                        buffer[k++] = info->header[n];
                    newline = 0/*false*/;
                }
                if (++n >= m)
                    break;
            }
            buffer[k] = '\0'/*just in case*/;
            if (!s_SockWrite(sock, buffer, k))
                SENDMAIL_RETURN(21, "Write error while sending custom header");
        }
        if (n < m)
            SENDMAIL_RETURN(22, "Header write error");
        if (!newline && !s_SockWrite(sock, MX_CRLF, 2))
            SENDMAIL_RETURN(23, "Write error while finalizing custom header");
    }

    if (body) {
        size_t n = 0, m = info->body_size ? info->body_size : strlen(body);
        int/*bool*/ newline = 0/*false*/;
        if (!(info->mx_options & fSendMail_NoMxHeader)  &&  m) {
            if (!s_SockWrite(sock, MX_CRLF, 2))
                SENDMAIL_RETURN(24, "Write error in message body delimiter");
        }
        while (n < m) {
            size_t k = 0;
            if (SOCK_Wait(sock, eIO_Read, &zero) != eIO_Timeout)
                break;
            while (k < sizeof(buffer) - sizeof(MX_CRLF)) {
                if (body[n] == '\n') {
                    memcpy(&buffer[k], MX_CRLF, sizeof(MX_CRLF) - 1);
                    k += sizeof(MX_CRLF) - 1;
                    newline = 1/*true*/;
                } else {
                    if (body[n] != '\r'  ||  (n+1 < m  &&  body[n+1] != '\n')){
                        if (body[n] == '.'  &&  (newline  ||  !n)) {
                            buffer[k++] = '.';
                            buffer[k++] = '.';
                        } else
                            buffer[k++] = body[n];
                    }
                    newline = 0/*false*/;
                }
                if (++n >= m)
                    break;
            }
            buffer[k] = '\0'/*just in case*/;
            if (!s_SockWrite(sock, buffer, k))
                SENDMAIL_RETURN(25, "Write error while sending message body");
        }
        if (n < m)
            SENDMAIL_RETURN(26, "Body write error");
        if ((!newline  &&  m  &&  !s_SockWrite(sock, MX_CRLF, 2))
            ||  !s_SockWrite(sock, "." MX_CRLF, 1 + 2)) {
            SENDMAIL_RETURN(27, "Write error while finalizing message body");
        }
    } else if (!s_SockWrite(sock, "." MX_CRLF, 1 + 2))
        SENDMAIL_RETURN(28, "Write error while finalizing message");

    if (!SENDMAIL_READ_RESPONSE(250, 0, buffer))
        SENDMAIL_RETURN2(29, "Protocol error in sending message", buffer);

    if (!s_SockWrite(sock, "QUIT" MX_CRLF, 0))
        SENDMAIL_RETURN(30, "Write error in QUIT command");
    if (!SENDMAIL_READ_RESPONSE(221, 0, buffer))
        SENDMAIL_RETURN2(31, "Protocol error in QUIT command", buffer);

    SOCK_Close(sock);
    return 0;
}