/** * \param tds the famous socket * \param buffer data to send * \param buflen bytes in buffer * \param last 1 if this is the last packet, else 0 * \return length written (>0), <0 on failure */ int tds_goodwrite(TDSSOCKET * tds, const unsigned char *buffer, size_t buflen) { int len; size_t sent = 0; assert(tds && buffer); while (sent < buflen) { /* TODO if send buffer is full we block receive !!! */ len = tds_select(tds, TDSSELWRITE, tds->query_timeout); if (len > 0) { len = tds_socket_write(tds->conn, tds, buffer + sent, buflen - sent); if (len == 0) continue; if (len < 0) return len; sent += len; continue; } /* error */ if (len < 0) { int err = sock_errno; char *errstr; if (TDSSOCK_WOULDBLOCK(err)) /* shouldn't happen, but OK, retry */ continue; errstr = sock_strerror(err); tdsdump_log(TDS_DBG_NETWORK, "select(2) failed: %d (%s)\n", err, errstr); sock_strerror_free(errstr); tds_connection_close(tds->conn); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, err); return -1; } /* timeout */ tdsdump_log(TDS_DBG_NETWORK, "tds_goodwrite(): timed out, asking client\n"); switch (tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: break; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } } return (int) sent; }
/** * Loops until we have received some characters * return -1 on failure */ int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen) { if (tds == NULL || buf == NULL || buflen < 1) return -1; for (;;) { int len, err; /* FIXME this block writing from other sessions */ len = tds_select(tds, TDSSELREAD, tds->query_timeout); #if !ENABLE_ODBC_MARS if (len > 0 && (len & TDSPOLLURG)) { char buf[32]; READSOCKET(tds->conn->s_signaled, buf, sizeof(buf)); /* send cancel */ if (!tds->in_cancel) tds_put_cancel(tds); continue; } #endif if (len > 0) { len = tds_socket_read(tds->conn, tds, buf, buflen); if (len == 0) continue; return len; } /* error */ if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; err = sock_errno; tds_connection_close(tds->conn); tdserror(tds_get_ctx(tds), tds, TDSEREAD, err); return -1; } /* timeout */ switch (tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: break; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } } }
int pool_write(TDS_SYS_SOCKET sock, const void *buf, size_t len) { int ret; const unsigned char *p = (const unsigned char *) buf; while (len) { ret = WRITESOCKET(sock, p, len); if (ret <= 0) { int err = errno; if (TDSSOCK_WOULDBLOCK(err) || err == EINTR) break; return -1; } p += ret; len -= ret; } return p - (const unsigned char *) buf; }
/** * Write to an OS socket * @returns 0 if blocking, <0 error >0 bytes readed */ static int tds_socket_write(TDSCONNECTION *conn, TDSSOCKET *tds, const unsigned char *buf, int buflen) { int err, len; char *errstr; #if ENABLE_EXTRA_CHECKS /* this simulate the fact that send can return less bytes */ if (buflen >= 5) { static int cnt = 0; if (++cnt == 5) { cnt = 0; buflen -= 3; } } #endif #if defined(__APPLE__) && defined(SO_NOSIGPIPE) len = send(conn->s, buf, buflen, 0); #else len = WRITESOCKET(conn->s, buf, buflen); #endif if (len > 0) return len; err = sock_errno; if (0 == len || TDSSOCK_WOULDBLOCK(err)) return 0; assert(len < 0); /* detect connection close */ errstr = sock_strerror(err); tdsdump_log(TDS_DBG_NETWORK, "send(2) failed: %d (%s)\n", err, errstr); sock_strerror_free(errstr); tds_connection_close(conn); tdserror(conn->tds_ctx, tds, TDSEWRIT, err); return -1; }
bool pool_packet_read(TDSSOCKET *tds) { int packet_len; int readed, err; tdsdump_log(TDS_DBG_INFO1, "tds in_len %d in_pos %d\n", tds->in_len, tds->in_pos); /* determine how much we should read */ packet_len = 8; if (tds->in_len >= 4) packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (tds->in_len >= packet_len) { tds->in_pos = 0; tds->in_len = 0; packet_len = 8; } tdsdump_log(TDS_DBG_INFO1, "packet_len %d capacity %d\n", packet_len, tds->recv_packet->capacity); assert(packet_len > tds->in_len); assert(packet_len <= tds->recv_packet->capacity); assert(tds->in_len < tds->recv_packet->capacity); readed = read(tds_get_s(tds), &tds->in_buf[tds->in_len], packet_len - tds->in_len); tdsdump_log(TDS_DBG_INFO1, "readed %d\n", readed); if (readed == 0) { /* socket closed */ tds->in_len = 0; return false; } if (readed < 0) { /* error */ err = sock_errno; if (err == EINTR || TDSSOCK_WOULDBLOCK(err)) return true; goto failure; } tds->in_len += readed; if (tds->in_len >= 4) { packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (packet_len < 8) goto failure; tdsdump_log(TDS_DBG_INFO1, "packet_len %d in_len %d after\n", packet_len, tds->in_len); /* resize packet if not enough */ if (packet_len > tds->recv_packet->capacity) { TDSPACKET *packet; packet = tds_realloc_packet(tds->recv_packet, packet_len); if (!packet) goto failure; tds->in_buf = packet->buf; tds->recv_packet = packet; } CHECK_TDS_EXTRA(tds); return tds->in_len < packet_len; } return true; failure: tds->in_len = -1; return false; }
/** * Read part of packet. Function does not block. * @return true if packet is not complete and we must call again, * false on full packet or error. */ bool pool_packet_read(TDSSOCKET *tds) { int packet_len; int readed, err; tdsdump_log(TDS_DBG_INFO1, "tds in_len %d in_pos %d\n", tds->in_len, tds->in_pos); // TODO MARS /* determine packet size */ packet_len = tds->in_len >= 4 ? TDS_GET_A2BE(&tds->in_buf[2]) : 8; if (TDS_UNLIKELY(packet_len < 8)) { tds->in_len = 0; return false; } /* get another packet */ if (tds->in_len >= packet_len) { tds->in_pos = 0; tds->in_len = 0; } for (;;) { /* determine packet size */ packet_len = 8; if (tds->in_len >= 4) { packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (packet_len < 8) break; tdsdump_log(TDS_DBG_INFO1, "packet_len %d in_len %d\n", packet_len, tds->in_len); /* resize packet if not enough */ if (packet_len > tds->recv_packet->capacity) { TDSPACKET *packet; packet = tds_realloc_packet(tds->recv_packet, packet_len); if (!packet) break; tds->in_buf = packet->buf; tds->recv_packet = packet; } CHECK_TDS_EXTRA(tds); if (tds->in_len >= packet_len) return false; } assert(packet_len > tds->in_len); assert(packet_len <= tds->recv_packet->capacity); assert(tds->in_len < tds->recv_packet->capacity); readed = read(tds_get_s(tds), &tds->in_buf[tds->in_len], packet_len - tds->in_len); tdsdump_log(TDS_DBG_INFO1, "readed %d\n", readed); /* socket closed */ if (readed == 0) break; /* error */ if (readed < 0) { err = sock_errno; if (err == EINTR) continue; if (TDSSOCK_WOULDBLOCK(err)) return true; break; } /* got some data */ tds->in_len += readed; } /* failure */ tds->in_len = 0; return false; }
static int tds_goodwrite(TDSSOCKET * tds, const unsigned char *buffer, int len, unsigned char last) { double start, now; const unsigned char *p = buffer; int rc; assert(tds && buffer); if (TDS_IS_SOCKET_INVALID(tds->s)) return -1; while (p - buffer < len) { start = GetTimeMark(); now = start; if ((rc = tds_select(tds, TDSSELWRITE, tds->query_timeout, start)) > 0) { int err; size_t remaining = len - (p - buffer); #ifdef USE_MSGMORE ssize_t nput = send(tds->s, p, remaining, last ? MSG_NOSIGNAL : MSG_NOSIGNAL|MSG_MORE); /* In case the kernel does not support MSG_MORE, try again without it */ if (nput < 0 && errno == EINVAL && !last) nput = send(tds->s, p, remaining, MSG_NOSIGNAL); #elif defined(__APPLE__) && defined(SO_NOSIGPIPE) ssize_t nput = send(tds->s, p, remaining, 0); #else ssize_t nput = WRITESOCKET(tds->s, p, remaining); #endif if (nput > 0) { p += nput; continue; } err = sock_errno; if (0 == nput || TDSSOCK_WOULDBLOCK(err) || err == TDSSOCK_EINTR) continue; assert(nput < 0); tdsdump_log(TDS_DBG_NETWORK, "send(2) failed: %d (%s)\n", err, strerror(err)); tds_report_error(tds->tds_ctx, tds, err, 20017, "Write to SQL Server failed"); tds_close_socket(tds); return -1; } else if (rc < 0) { int err = sock_errno; if (TDSSOCK_WOULDBLOCK(err)) /* shouldn't happen, but OK, retry */ continue; tdsdump_log(TDS_DBG_NETWORK, "select(2) failed: %d (%s)\n", err, strerror(err)); tds_report_error(tds->tds_ctx, tds, err, 20005, "select/send finished with error"); tds_close_socket(tds); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout && (now - start) >= tds->query_timeout) { tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout"); tds_close_socket(tds); return -1; } tdsdump_log(TDS_DBG_NETWORK, "tds_goodwrite(): timed out, asking client\n"); switch (rc = tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout")) { case TDS_INT_CONTINUE: continue; case TDS_INT_TIMEOUT: /* * "Cancel the operation ... but leave the dbproc in working condition." * We must try to send the cancel packet, else we have to abandon the dbproc. * If it can't be done, a harder error e.g. ECONNRESET will bubble up. */ tds_send_cancel(tds); continue; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } assert(0); /* not reached */ } assert(0); /* not reached */ } #ifdef USE_CORK /* force packet flush */ if (last) { int opt; opt = 0; setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt)); opt = 1; setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt)); } #endif return len; }
/** * Loops until we have received buflen characters * return -1 on failure */ static int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen, unsigned char unfinished) { double start, global_start; int got = 0; int canceled = 0; if (buf == NULL || buflen < 1 || IS_TDSDEAD(tds)) return 0; global_start = start = GetTimeMark(); while (buflen > 0) { int len; double now; if (IS_TDSDEAD(tds)) return -1; if ((len = tds_select(tds, TDSSELREAD, tds->query_timeout, global_start)) > 0) { len = READSOCKET(tds->s, buf + got, buflen); if (len < 0 && TDSSOCK_WOULDBLOCK(sock_errno)) continue; /* detect connection close */ if (len <= 0) { tds_close_socket(tds); if (len == 0) { tds_client_msg(tds->tds_ctx, tds, 20011, 6, 0, 0, "EOF in the socket."); } else { tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); } return -1; } } else if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; tds_close_socket(tds); tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout > 0 && now - start >= tds->query_timeout) { int timeout_action = TDS_INT_CONTINUE; if (canceled) return got ? got : -1; if (tds->query_timeout_func && tds->query_timeout) timeout_action = (*tds->query_timeout_func) (tds->query_timeout_param, (int)(now - global_start)); switch (timeout_action) { case TDS_INT_EXIT: exit(EXIT_FAILURE); break; case TDS_INT_CANCEL: tds_send_cancel(tds); canceled = 1; /* fall through to wait while cancelling happens */ case TDS_INT_CONTINUE: start = now; default: break; } } } buflen -= len; got += len; if (unfinished && got) return got; } return got; }