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; }
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 CSocket::SetTimeout(EIO_Event event, const STimeout* timeout) { if (timeout == kDefaultTimeout) return eIO_Success; switch (event) { case eIO_Open: if ( timeout ) { if (&oo_timeout != timeout) oo_timeout = *timeout; o_timeout = &oo_timeout; } else o_timeout = 0; break; case eIO_Read: if ( timeout ) { if (&rr_timeout != timeout) rr_timeout = *timeout; r_timeout = &rr_timeout; } else r_timeout = 0; break; case eIO_Write: if ( timeout ) { if (&ww_timeout != timeout) ww_timeout = *timeout; w_timeout = &ww_timeout; } else w_timeout = 0; break; case eIO_ReadWrite: if ( timeout ) { if (&rr_timeout != timeout) rr_timeout = *timeout; r_timeout = &rr_timeout; if (&ww_timeout != timeout) ww_timeout = *timeout; w_timeout = &ww_timeout; } else { r_timeout = 0; w_timeout = 0; } break; case eIO_Close: if ( timeout ) { if (&cc_timeout != timeout) cc_timeout = *timeout; c_timeout = &cc_timeout; } else c_timeout = 0; break; default: return eIO_InvalidArg; } return m_Socket ? SOCK_SetTimeout(m_Socket, event, timeout) : eIO_Success; }
/* 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; }
static EIO_Status s_VT_Read (CONNECTOR connector, void* buf, size_t size, size_t* n_read, const STimeout* timeout) { SSockConnector* xxx = (SSockConnector*) connector->handle; assert(xxx->sock); verify(SOCK_SetTimeout(xxx->sock, eIO_Read, timeout) == eIO_Success); return SOCK_Read(xxx->sock, buf, size, n_read, eIO_ReadPlain); }
static EIO_Status s_VT_Write (CONNECTOR connector, const void* buf, size_t size, size_t* n_written, const STimeout* timeout) { SSockConnector* xxx = (SSockConnector*) connector->handle; assert(xxx->sock); verify(SOCK_SetTimeout(xxx->sock, eIO_Write, timeout) == eIO_Success); return SOCK_Write(xxx->sock, buf, size, n_written, eIO_WritePlain); }
/* 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"); }
/* Prepare connector for reading. Open socket if necessary and * make initial connect and send, re-trying if possible until success. * Return codes: * eIO_Success = success, connector is ready for reading (uuu->sock != NULL); * eIO_Timeout = maybe (check uuu->sock) connected and no data yet available; * other code = error, not connected (uuu->sock == NULL). */ static EIO_Status s_PreRead(SHttpConnector* uuu, const STimeout* timeout, EReadMode read_mode) { EIO_Status status; char* retry; for (;;) { status = s_ConnectAndSend(uuu, read_mode); if (!uuu->sock) { assert(status != eIO_Success); break; } if (status != eIO_Success) { if (status != eIO_Timeout || status == SOCK_Status(uuu->sock, eIO_Read)/*pending*/) break; } /* set timeout */ SOCK_SetTimeout(uuu->sock, eIO_Read, timeout); if (!uuu->read_header) break; if ((status = s_ReadHeader(uuu, &retry, read_mode)) == eIO_Success) { size_t w_size = BUF_Size(uuu->w_buf); assert(!uuu->read_header && !retry); /* pending output data no longer needed */ if (BUF_Read(uuu->w_buf, 0, w_size) != w_size) { CORE_LOG_X(15, eLOG_Error, "[HTTP] Cannot discard output buffer"); assert(0); } break; } /* if polling then bail out with eIO_Timeout */ if(status == eIO_Timeout && timeout && !(timeout->sec | timeout->usec)) break; /* HTTP header read error; disconnect and try to use another server */ SOCK_Abort(uuu->sock); s_DropConnection(uuu, 0/*no wait*/); if (!s_Adjust(uuu, &retry, read_mode)) { uuu->can_connect = eCC_None; break; } } return status; }
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; }
/* Parse HTTP header */ static EIO_Status s_ReadHeader(SHttpConnector* uuu, char** retry, EReadMode read_mode) { ERetry redirect = eRetry_None; int server_error = 0; int http_status; EIO_Status status; char* header; size_t size; assert(uuu->sock && uuu->read_header); *retry = 0; /* line by line HTTP header input */ for (;;) { /* do we have full header yet? */ size = BUF_Size(uuu->http); if (!(header = (char*) malloc(size + 1))) { CORE_LOGF_X(7, eLOG_Error, ("[HTTP] Cannot allocate header, %lu bytes", (unsigned long) size)); return eIO_Unknown; } verify(BUF_Peek(uuu->http, header, size) == size); header[size] = '\0'; if (size >= 4 && strcmp(&header[size - 4], "\r\n\r\n") == 0) break/*full header captured*/; free(header); status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, 0); if (status != eIO_Success) { ELOG_Level level; if (status == eIO_Timeout) { const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read); if (!tmo) level = eLOG_Error; else if (tmo->sec | tmo->usec) level = eLOG_Warning; else if (read_mode != eRM_WaitCalled) level = eLOG_Trace; else return status; } else level = eLOG_Error; CORE_LOGF_X(8, level, ("[HTTP] Error reading header (%s)", IO_StatusStr(status))); return status; } } /* the entire header has been read */ uuu->read_header = 0/*false*/; status = eIO_Success; if (BUF_Read(uuu->http, 0, size) != size) { CORE_LOG_X(9, eLOG_Error, "[HTTP] Cannot discard HTTP header buffer"); status = eIO_Unknown; assert(0); } /* HTTP status must come on the first line of the response */ if (sscanf(header, "HTTP/%*d.%*d %d ", &http_status) != 1 || !http_status) http_status = -1; if (http_status < 200 || 299 < http_status) { server_error = http_status; if (http_status == 301 || http_status == 302) redirect = eRetry_Redirect; else if (http_status == 401) redirect = eRetry_Authenticate; else if (http_status == 407) redirect = eRetry_ProxyAuthenticate; else if (http_status < 0 || http_status == 403 || http_status == 404) uuu->net_info->max_try = 0; } uuu->code = http_status < 0 ? -1 : http_status; if ((server_error || !uuu->error_header) && uuu->net_info->debug_printout == eDebugPrintout_Some) { /* HTTP header gets printed as part of data logging when uuu->net_info->debug_printout == eDebugPrintout_Data. */ const char* header_header; if (!server_error) header_header = "HTTP header"; else if (uuu->flags & fHCC_KeepHeader) header_header = "HTTP header (error)"; else if (uuu->code == 301 || uuu->code == 302) header_header = "HTTP header (moved)"; else if (!uuu->net_info->max_try) header_header = "HTTP header (unrecoverable error)"; else header_header = "HTTP header (server error, can retry)"; CORE_DATA_X(19, header, size, header_header); } {{ /* parsing "NCBI-Message" tag */ static const char kNcbiMessageTag[] = "\n" HTTP_NCBI_MESSAGE " "; const char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s, kNcbiMessageTag, sizeof(kNcbiMessageTag)-1)==0){ const char* message = s + sizeof(kNcbiMessageTag) - 1; while (*message && isspace((unsigned char)(*message))) message++; if (!(s = strchr(message, '\r'))) s = strchr(message, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > message); if (message != s) { if (s_MessageHook) { if (s_MessageIssued <= 0) { s_MessageIssued = 1; s_MessageHook(message); } } else { s_MessageIssued = -1; CORE_LOGF_X(10, eLOG_Critical, ("[NCBI-MESSAGE] %.*s", (int)(s - message), message)); } } break; } } }} if (uuu->flags & fHCC_KeepHeader) { if (!BUF_Write(&uuu->r_buf, header, size)) { CORE_LOG_X(11, eLOG_Error, "[HTTP] Cannot keep HTTP header"); status = eIO_Unknown; } free(header); return status; } if (uuu->parse_http_hdr && !(*uuu->parse_http_hdr)(header, uuu->adjust_data, server_error)) { server_error = 1/*fake, but still boolean true*/; } if (redirect == eRetry_Redirect) { /* parsing "Location" pointer */ static const char kLocationTag[] = "\nLocation: "; char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) { char* location = s + sizeof(kLocationTag) - 1; while (*location && isspace((unsigned char)(*location))) location++; if (!(s = strchr(location, '\r'))) s = strchr(location, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > location); if (s != location) { size_t len = (size_t)(s - location); memmove(header, location, len); header[len] = '\0'; *retry = header; } break; } } } else if (redirect != eRetry_None) { /* parsing "Authenticate" tags */ static const char kAuthenticateTag[] = "-Authenticate: "; char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (strncasecmp(s + (redirect == eRetry_Authenticate ? 4 : 6), kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0){ if ((redirect == eRetry_Authenticate && strncasecmp(s, "\nWWW", 4) != 0) || (redirect == eRetry_ProxyAuthenticate && strncasecmp(s, "\nProxy", 6) != 0)) { continue; } /* TODO */ } } } else if (!server_error) { static const char kContentLengthTag[] = "\nContent-Length: "; const char* s; for (s = strchr(header, '\n'); s && *s; s = strchr(s + 1, '\n')) { if (!strncasecmp(s,kContentLengthTag,sizeof(kContentLengthTag)-1)){ const char* expected = s + sizeof(kContentLengthTag) - 1; while (*expected && isspace((unsigned char)(*expected))) expected++; if (!(s = strchr(expected, '\r'))) s = strchr(expected, '\n'); assert(s); do { if (!isspace((unsigned char) s[-1])) break; } while (--s > expected); if (s != expected) { char* e; errno = 0; uuu->expected = (size_t) strtol(expected, &e, 10); if (errno || e != s) uuu->expected = 0; else if (!uuu->expected) uuu->expected = (size_t)(-1L); } break; } } } if (!*retry) free(header); /* skip & printout the content, if server error was flagged */ if (server_error && uuu->net_info->debug_printout == eDebugPrintout_Some) { BUF buf = 0; char* body; SOCK_SetTimeout(uuu->sock, eIO_Read, 0); /* read until EOF */ SOCK_StripToPattern(uuu->sock, 0, 0, &buf, 0); if (!(size = BUF_Size(buf))) { CORE_LOG_X(12, eLOG_Trace, "[HTTP] No HTTP body received with this error"); } else if ((body = (char*) malloc(size)) != 0) { size_t n = BUF_Read(buf, body, size); if (n != size) { CORE_LOGF_X(13, eLOG_Error, ("[HTTP] Cannot read server error " "body from buffer (%lu out of %lu)", (unsigned long) n, (unsigned long) size)); } CORE_DATA_X(20, body, n, "Server error body"); free(body); } else { CORE_LOGF_X(14, eLOG_Error, ("[HTTP] Cannot allocate server error " "body, %lu bytes", (unsigned long) size)); } BUF_Destroy(buf); } return server_error ? eIO_Unknown : status; }
/* Connect to the server specified by uuu->net_info, then compose and form * relevant HTTP header, and flush the accumulated output data(uuu->w_buf) * after the HTTP header. If connection/write unsuccessful, retry to reconnect * and send the data again until permitted by s_Adjust(). */ static EIO_Status s_ConnectAndSend(SHttpConnector* uuu, EReadMode read_mode) { EIO_Status status; for (;;) { char* null = 0; if (!uuu->sock) { if ((status = s_Connect(uuu, read_mode)) != eIO_Success) break; assert(uuu->sock); uuu->read_header = 1/*true*/; uuu->shut_down = 0/*false*/; uuu->expected = 0; uuu->received = 0; uuu->code = 0; } else status = eIO_Success; if (uuu->w_len) { size_t off = BUF_Size(uuu->w_buf) - uuu->w_len; SOCK_SetTimeout(uuu->sock, eIO_Write, uuu->w_timeout); do { char buf[4096]; size_t n_written; size_t n_write = BUF_PeekAt(uuu->w_buf, off, buf, sizeof(buf)); status = SOCK_Write(uuu->sock, buf, n_write, &n_written, eIO_WritePlain); if (status != eIO_Success) break; uuu->w_len -= n_written; off += n_written; } while (uuu->w_len); } else if (!uuu->shut_down) status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain); if (status == eIO_Success) { assert(uuu->w_len == 0); if (!uuu->shut_down) { /* 10/07/03: While this call here is perfectly legal, it could * cause connection severed by a buggy CISCO load-balancer. */ /* 10/28/03: CISCO's beta patch for their LB shows that the * problem has been fixed; no more 2'30" drops in connections * that shut down for write. We still leave this commented * out to allow unpatched clients to work seamlessly... */ /*SOCK_Shutdown(uuu->sock, eIO_Write);*/ uuu->shut_down = 1; } break; } if (status == eIO_Timeout && uuu->w_timeout && !(uuu->w_timeout->sec | uuu->w_timeout->usec)) { break; } CORE_LOGF_X(6, eLOG_Error, ("[HTTP] Error writing body at offset %lu (%s)", (unsigned long)(BUF_Size(uuu->w_buf) - uuu->w_len), IO_StatusStr(status))); /* write failed; close and try to use another server */ SOCK_Abort(uuu->sock); s_DropConnection(uuu, 0/*no wait*/); if (!s_Adjust(uuu, &null, read_mode)) { uuu->can_connect = eCC_None; status = eIO_Closed; break; } } return status; }
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; } } }
static void TEST__client_2(SOCK sock) { #define W_FIELD 10 #define N_FIELD 1000 #define N_REPEAT 10 #define N_RECONNECT 3 EIO_Status status; size_t n_io, n_io_done, i; char buf[W_FIELD * N_FIELD + 1]; CORE_LOGF(eLOG_Note, ("TEST__client_2(TC2) @:%hu", SOCK_GetLocalPort(sock, eNH_HostByteOrder))); /* fill out a buffer to send to server */ memset(buf, 0, sizeof(buf)); for (i = 0; i < N_FIELD; i++) { sprintf(buf + i * W_FIELD, "%10lu", (unsigned long)i); } /* send the buffer to server, then get it back */ for (i = 0; i < N_REPEAT; i++) { char buf1[sizeof(buf)]; STimeout w_to, r_to; int/*bool*/ w_timeout_on = (int)(i%2); /* if to start from */ int/*bool*/ r_timeout_on = (int)(i%2); /* zero or inf. timeout */ char* x_buf; /* set timeout */ w_to.sec = 0; w_to.usec = 0; status = SOCK_SetTimeout(sock, eIO_Write, w_timeout_on ? &w_to : 0); assert(status == eIO_Success); /* reconnect */ if ((i % N_RECONNECT) == 0) { size_t j = i / N_RECONNECT; do { SOCK_SetDataLogging(sock, eOn); status = SOCK_Reconnect(sock, 0, 0, 0); SOCK_SetDataLogging(sock, eDefault); CORE_LOGF(eLOG_Note, ("TC2::reconnect @:%hu: i=%lu, j=%lu, status=%s", SOCK_GetLocalPort(sock, eNH_HostByteOrder), (unsigned long) i, (unsigned long) j, IO_StatusStr(status))); assert(status == eIO_Success); assert(SOCK_Status(sock, eIO_Read) == eIO_Success); assert(SOCK_Status(sock, eIO_Write) == eIO_Success); /* give a break to let server reset the listening socket */ X_SLEEP(1); } while ( j-- ); } /* send */ x_buf = buf; n_io = sizeof(buf); do { X_SLEEP(1); status = SOCK_Write(sock, x_buf, n_io, &n_io_done, eIO_WritePersist); if (status == eIO_Closed) CORE_LOG(eLOG_Fatal, "TC2::write: connection closed"); CORE_LOGF(eLOG_Note, ("TC2::write:" " i=%d, status=%7s, n_io=%5lu, n_io_done=%5lu" " timeout(%d): %5u.%06us", (int) i, IO_StatusStr(status), (unsigned long) n_io, (unsigned long) n_io_done, (int) w_timeout_on, w_to.sec, w_to.usec)); if ( !w_timeout_on ) { assert(status == eIO_Success && n_io_done == n_io); } else { const STimeout* x_to; assert(status == eIO_Success || status == eIO_Timeout); x_to = SOCK_GetTimeout(sock, eIO_Write); assert(w_to.sec == x_to->sec && w_to.usec == x_to->usec); } n_io -= n_io_done; x_buf += n_io_done; if (status == eIO_Timeout) s_DoubleTimeout(&w_to); status = SOCK_SetTimeout(sock, eIO_Write, &w_to); assert(status == eIO_Success); w_timeout_on = 1/*true*/; } while ( n_io ); /* get back the just sent data */ r_to.sec = 0; r_to.usec = 0; status = SOCK_SetTimeout(sock, eIO_Read, r_timeout_on ? &r_to : 0); assert(status == eIO_Success); x_buf = buf1; n_io = sizeof(buf1); do { if (i%2 == 0) { /* peek a little piece twice and compare */ char xx_buf1[128], xx_buf2[128]; size_t xx_io_done1, xx_io_done2; if (SOCK_Read(sock, xx_buf1, sizeof(xx_buf1), &xx_io_done1, eIO_ReadPeek) == eIO_Success && SOCK_Read(sock, xx_buf2, xx_io_done1, &xx_io_done2, eIO_ReadPeek) == eIO_Success) { assert(xx_io_done1 >= xx_io_done2); assert(memcmp(xx_buf1, xx_buf2, xx_io_done2) == 0); } } status = SOCK_Read(sock, x_buf, n_io, &n_io_done, eIO_ReadPlain); if (status == eIO_Closed) { assert(SOCK_Status(sock, eIO_Read) == eIO_Closed); CORE_LOG(eLOG_Fatal, "TC2::read: connection closed"); } CORE_LOGF(eLOG_Note, ("TC2::read: " " i=%d, status=%7s, n_io=%5lu, n_io_done=%5lu" " timeout(%d): %5u.%06us", (int) i, IO_StatusStr(status), (unsigned long) n_io, (unsigned long) n_io_done, (int) r_timeout_on, r_to.sec, r_to.usec)); if ( !r_timeout_on ) { assert(status == eIO_Success && n_io_done > 0); } else { const STimeout* x_to; assert(status == eIO_Success || status == eIO_Timeout); x_to = SOCK_GetTimeout(sock, eIO_Read); assert(r_to.sec == x_to->sec && r_to.usec == x_to->usec); } n_io -= n_io_done; x_buf += n_io_done; if (status == eIO_Timeout) s_DoubleTimeout(&r_to); status = SOCK_SetTimeout(sock, eIO_Read, &r_to); assert(status == eIO_Success); r_timeout_on = 1/*true*/; } while ( n_io ); assert(memcmp(buf, buf1, sizeof(buf)) == 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; }