Пример #1
0
/* 
 * pool_process_members
 * check the fd_set for members returning data to the client, lookup the 
 * client holding this member and forward the results.
 */
int
pool_process_members(TDS_POOL * pool, fd_set * fds)
{
	TDS_POOL_MEMBER *pmbr;
	TDS_POOL_USER *puser;
	TDSSOCKET *tds;
	int i, age, ret;
	int cnt = 0;
	time_t time_now;

	for (i = 0; i < pool->num_members; i++) {
		pmbr = &pool->members[i];

		tds = pmbr->tds;
		if (!tds) {
			assert(pmbr->state == TDS_IDLE);
			continue;	/* dead connection */
		}

		time_now = time(NULL);
		if (FD_ISSET(tds_get_s(tds), fds)) {
			pmbr->last_used_tm = time_now;
			cnt++;
			if (pool_packet_read(tds))
				continue;

			if (tds->in_len == 0) {
				fprintf(stderr, "Uh oh! member %d disconnected\n", i);
				/* mark as dead */
				pool_free_member(pmbr);
			} else if (tds->in_len < 0) {
				fprintf(stderr, "Uh oh! member %d disconnected\n", i);
				perror("read");
				pool_free_member(pmbr);
			} else {
				tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from server:", tds->in_buf, tds->in_len);
				/* fprintf(stderr, "read %d bytes from member %d\n", tds->in_len, i); */
				puser = pmbr->current_user;
				if (puser) {
					tdsdump_log(TDS_DBG_INFO1, "writing it sock %d\n", tds_get_s(puser->tds));
					/* cf. net.c for better technique.  */
					/* FIXME handle partial write, stop read on member */
					ret = pool_write_all(tds_get_s(puser->tds), tds->in_buf, tds->in_len);
					if (ret < 0) { /* couldn't write, ditch the user */
						fprintf(stdout, "member %d received error while writing\n",i);
						pool_free_member(pmbr);
					}
				}
			}
		}
		age = time_now - pmbr->last_used_tm;
		if (age > pool->max_member_age && i >= pool->min_open_conn && !pmbr->current_user) {
			fprintf(stderr, "member %d is %d seconds old...closing\n", i, age);
			pool_free_member(pmbr);
		}
	}
	return cnt;
}
Пример #2
0
/*
 * pool_user_read
 * checks the packet type of data coming from the client and allocates a 
 * pool member if necessary.
 */
static bool
pool_user_read(TDS_POOL * pool, TDS_POOL_USER * puser)
{
	TDSSOCKET *tds = puser->sock.tds;
	TDS_POOL_MEMBER *pmbr = NULL;

	for (;;) {
		if (pool_packet_read(tds))
			break;
		if (tds->in_len == 0) {
			tdsdump_log(TDS_DBG_INFO1, "user disconnected\n");
			pool_free_user(pool, puser);
			return false;
		} else {
			TDS_UCHAR in_flag = tds->in_buf[0];

			tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from client:", tds->in_buf, tds->in_len);

			switch (in_flag) {
			case TDS_QUERY:
			case TDS_NORMAL:
			case TDS_RPC:
			case TDS_BULK:
			case TDS_CANCEL:
			case TDS7_TRANS:
				if (!pool_write_data(&puser->sock, &puser->assigned_member->sock)) {
					pool_reset_member(pool, puser->assigned_member);
					return false;
				}
				pmbr = puser->assigned_member;
				break;

			default:
				tdsdump_log(TDS_DBG_ERROR, "Unrecognized packet type, closing user\n");
				pool_free_user(pool, puser);
				return false;
			}
		}
	}
	if (pmbr && !pmbr->sock.poll_send)
		tds_socket_flush(tds_get_s(pmbr->sock.tds));
	return true;
}
Пример #3
0
/*
 * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary
 * is not aligned with the character boundary.  In that event, it will return an error, and
 * some number of bytes (less than a character) will remain in the tail end of temp[].  They are  
 * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read.
 */
static int
read_and_convert(TDSSOCKET * tds, const TDSICONV * char_conv, size_t * wire_size, char **outbuf,
		 size_t * outbytesleft)
{
	TEMP_INIT(256);
	/*
	 * temp (above) is the "preconversion" buffer, the place where the UCS-2 data 
	 * are parked before converting them to ASCII.  It has to have a size, 
	 * and there's no advantage to allocating dynamically.  
	 * This also avoids any memory allocation error.  
	 */
	const char *bufp;
	size_t bufleft = 0;
	const size_t max_output = *outbytesleft;

	/* cast away const for message suppression sub-structure */
	TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;

	memset(suppress, 0, sizeof(char_conv->suppress));
	
	for (bufp = temp; *wire_size > 0 && *outbytesleft > 0; bufp = temp + bufleft) {
		assert(bufp >= temp);
		/* read a chunk of data */
		bufleft = TEMP_SIZE - bufleft;
		if (bufleft > *wire_size)
			bufleft = *wire_size;
		tds_get_n(tds, (char *) bufp, (int)bufleft);
		*wire_size -= bufleft;
		bufleft += bufp - temp;

		/* Convert chunk and write to dest. */
		bufp = temp; /* always convert from start of buffer */
		suppress->einval = *wire_size > 0; /* EINVAL matters only on the last chunk. */
		if ((size_t)-1 == tds_iconv(tds, char_conv, to_client, &bufp, &bufleft, outbuf, outbytesleft)) {
			tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: tds_iconv returned errno %d\n", errno);
			if (errno != EILSEQ) {
				tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: "
							     "Gave up converting %u bytes due to error %d.\n",
							     (unsigned int) bufleft, errno);
				tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", bufp, bufleft);
			}

			if (bufp == temp) {	/* tds_iconv did not convert anything, avoid infinite loop */
				tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: draining remaining %u bytes.\n",
							     (unsigned int) *wire_size);
				tds_get_n(tds, NULL, (int)(*wire_size)); /* perhaps we should read unconverted data into outbuf? */
				*wire_size = 0;
				break;
			}
				
			if (bufleft) {
				memmove(temp, bufp, bufleft);
			}
		}
	}

	assert(*wire_size == 0 || *outbytesleft == 0);

	TEMP_FREE;
	return (int)(max_output - *outbytesleft);
}
Пример #4
0
/**
 * Read in one 'packet' from the server.  This is a wrapped outer packet of
 * the protocol (they bundle result packets into chunks and wrap them at
 * what appears to be 512 bytes regardless of how that breaks internal packet
 * up.   (tetherow\@nol.org)
 * @return bytes read or -1 on failure
 */
int
tds_read_packet(TDSSOCKET * tds)
{
#if ENABLE_ODBC_MARS
	TDSCONNECTION *conn = tds->conn;

	tds_mutex_lock(&conn->list_mtx);

	for (;;) {
		int wait_res;
		TDSPACKET **p_packet;

		if (IS_TDSDEAD(tds)) {
			tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD\n");
			break;
		}

		/* if there is a packet for me return it */
		for (p_packet = &conn->packets; *p_packet; p_packet = &(*p_packet)->next)
			if ((*p_packet)->sid == tds->sid)
				break;

		if (*p_packet) {
			size_t hdr_size;

			/* remove our packet from list */
			TDSPACKET *packet = *p_packet;
			*p_packet = packet->next;
			tds_packet_cache_add(conn, tds->recv_packet);
			tds_mutex_unlock(&conn->list_mtx);

			packet->next = NULL;
			tds->recv_packet = packet;

			hdr_size = packet->buf[0] == TDS72_SMP ? sizeof(TDS72_SMP_HEADER) : 0;
			tds->in_buf = packet->buf + hdr_size;
			tds->in_len = packet->len - hdr_size;
			tds->in_pos  = 8;
			tds->in_flag = tds->in_buf[0];

			/* send acknowledge if needed */
			if (tds->recv_seq + 2 >= tds->recv_wnd)
				tds_update_recv_wnd(tds, tds->recv_seq + 4);

			return tds->in_len;
		}

		/* network ok ? process network */
		if (!conn->in_net_tds) {
			tds_connection_network(conn, tds, 0);
			continue;
		}

		/* wait local condition */
		wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout);
		if (wait_res == ETIMEDOUT
		    && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) {
			tds_mutex_unlock(&conn->list_mtx);
			tds_close_socket(tds);
			return -1;
		}
	}

	tds_mutex_unlock(&conn->list_mtx);
	return -1;
#else /* !ENABLE_ODBC_MARS */
	unsigned char *pkt = tds->in_buf, *p, *end;

	if (IS_TDSDEAD(tds)) {
		tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD");
		return -1;
	}

	tds->in_len = 0;
	tds->in_pos = 0;
	for (p = pkt, end = p+8; p < end;) {
		int len = tds_connection_read(tds, p, end - p);
		if (len <= 0) {
			tds_close_socket(tds);
			return -1;
		}

		p += len;
		if (p - pkt >= 4) {
			unsigned pktlen = TDS_GET_A2BE(pkt+2);
			/* packet must at least contains header */
			if (TDS_UNLIKELY(pktlen < 8)) {
				tds_close_socket(tds);
				return -1;
			}
			if (TDS_UNLIKELY(pktlen > tds->recv_packet->capacity)) {
				TDSPACKET *packet = tds_realloc_packet(tds->recv_packet, pktlen);
				if (TDS_UNLIKELY(!packet)) {
					tds_close_socket(tds);
					return -1;
				}
				tds->recv_packet = packet;
				pkt = packet->buf;
				p = pkt + (p-tds->in_buf);
				tds->in_buf = pkt;
			}
			end = pkt + pktlen;
		}
	}

	/* set the received packet type flag */
	tds->in_flag = pkt[0];

	/* Set the length and pos (not sure what pos is used for now */
	tds->in_len = p - pkt;
	tds->in_pos = 8;
	tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len);

	return tds->in_len;
#endif /* !ENABLE_ODBC_MARS */
}
Пример #5
0
static void
tds_connection_network(TDSCONNECTION *conn, TDSSOCKET *tds, int send)
{
	assert(!conn->in_net_tds);
	conn->in_net_tds = tds;
	tds_mutex_unlock(&conn->list_mtx);

	for (;;) {
		/* wait packets or update */
		int rc = tds_select(tds, conn->send_packets ? TDSSELREAD|TDSSELWRITE : TDSSELREAD, tds->query_timeout);

		if (rc < 0) {
			/* FIXME better error report */
			tds_connection_close(conn);
			break;
		}

		/* change notify */
		/* TODO async */

		if (!rc) { /* timeout */
			tdsdump_log(TDS_DBG_INFO1, "timeout\n");
			switch (rc = tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) {
			case TDS_INT_CONTINUE:
				continue;
			default:
			case TDS_INT_CANCEL:
				tds_close_socket(tds);
			}
			break;
		}

		/*
		 * we must write first to catch write errors as
		 * write errors, not as read
		 */
		/* something to send */
		if (conn->send_packets && (rc & POLLOUT) != 0) {
			TDSSOCKET *s;

			short sid = tds_packet_write(conn);
			if (sid == tds->sid) break;	/* return to caller */

			tds_mutex_lock(&conn->list_mtx);
			if (sid >= 0 && sid < conn->num_sessions) {
				s = conn->sessions[sid];
				if (TDSSOCKET_VALID(s))
					tds_cond_signal(&s->packet_cond);
			}
			tds_mutex_unlock(&conn->list_mtx);
			/* avoid using a possible closed connection */
			continue;
		}

		/* received */
		if (rc & POLLIN) {
			TDSPACKET *packet;
			TDSSOCKET *s;

			/* try to read a packet */
			tds_packet_read(conn, tds);
			packet = conn->recv_packet;
			if (!packet || conn->recv_pos < packet->len) continue;
			conn->recv_packet = NULL;
			conn->recv_pos = 0;

			tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", packet->buf, packet->len);

			tds_mutex_lock(&conn->list_mtx);
			if (packet->sid >= 0 && packet->sid < conn->num_sessions) {
				s = conn->sessions[packet->sid];
				if (TDSSOCKET_VALID(s)) {
					/* append to correct session */
					if (packet->buf[0] == TDS72_SMP && packet->buf[1] != TDS_SMP_DATA)
						tds_packet_cache_add(conn, packet);
					else
						tds_append_packet(&conn->packets, packet);
					packet = NULL;
					/* notify */
					tds_cond_signal(&s->packet_cond);
				}
			}
			tds_mutex_unlock(&conn->list_mtx);
			tds_free_packets(packet);
			/* if we are receiving return the packet */
			if (!send) break;
		}
	}

	tds_mutex_lock(&conn->list_mtx);
	conn->in_net_tds = NULL;
}
Пример #6
0
/* read partial packet */
static void
tds_packet_read(TDSCONNECTION *conn, TDSSOCKET *tds)
{
	TDSPACKET *packet = conn->recv_packet;
	int len;

	/* allocate some space to read data */
	if (!packet) {
		conn->recv_packet = packet = tds_get_packet(conn, MAX(conn->env.block_size + sizeof(TDS72_SMP_HEADER), 512));
		if (!packet) goto Memory_Error;
		TDS_MARK_UNDEFINED(packet->buf, packet->capacity);
		conn->recv_pos = 0;
		packet->len = 8;
	}

	assert(conn->recv_pos < packet->len && packet->len <= packet->capacity);

	len = tds_connection_read(tds, packet->buf + conn->recv_pos, packet->len - conn->recv_pos);
	if (len < 0)
		goto Severe_Error;
	conn->recv_pos += len;
	assert(conn->recv_pos <= packet->len && packet->len <= packet->capacity);

	/* handle SMP */
	if (conn->recv_pos > 0 && packet->buf[0] == TDS72_SMP) {
		TDS72_SMP_HEADER mars_header;
		short sid;
		TDSSOCKET *tds;
		TDS_UINT size;

		if (conn->recv_pos < 16) {
			packet->len = 16;
			return;
		}

		memcpy(&mars_header, packet->buf, sizeof(mars_header));
		tdsdump_dump_buf(TDS_DBG_HEADER, "Received MARS header", &mars_header, sizeof(mars_header));
		sid = TDS_GET_A2LE(&mars_header.sid);

		/* FIXME this is done even by caller !! */
		tds = NULL;
		tds_mutex_lock(&conn->list_mtx);
		if (sid >= 0 && sid < conn->num_sessions)
			tds = conn->sessions[sid];
		tds_mutex_unlock(&conn->list_mtx);
		packet->sid = sid;

		if (tds == BUSY_SOCKET) {
			if (mars_header.type != TDS_SMP_FIN) {
				tdsdump_log(TDS_DBG_ERROR, "Received MARS with no session (%d)\n", sid);
				goto Severe_Error;
			}

			/* check if was just a "zombie" socket */
			tds_mutex_lock(&conn->list_mtx);
			conn->sessions[sid] = NULL;
			tds_mutex_unlock(&conn->list_mtx);

			/* reset packet to initial state to reuse it */
			packet->len = 8;
			conn->recv_pos = 0;
			return;
		}

		if (!tds) {
			/* server sent a unknown packet, close connection */
			goto Severe_Error;
		}

		tds->send_wnd = TDS_GET_A4LE(&mars_header.wnd);
		size = TDS_GET_A4LE(&mars_header.size);
		if (mars_header.type == TDS_SMP_ACK) {
			if (size != sizeof(mars_header))
				goto Severe_Error;
		} else if (mars_header.type == TDS_SMP_DATA) {
			if (size < 0x18 || size > 0xffffu + sizeof(mars_header))
				goto Severe_Error;
			/* avoid recursive SMP */
			if (conn->recv_pos > 16 && packet->buf[16] == TDS72_SMP)
				goto Severe_Error;
			/* TODO is possible to put 2 TDS packet inside a single DATA ?? */
			if (conn->recv_pos >= 20 && TDS_GET_A2BE(&packet->buf[18]) != size - 16)
				goto Severe_Error;
			tds->recv_seq = TDS_GET_A4LE(&mars_header.seq);
			/*
			 * does not sent ACK here cause this would lead to memory waste
			 * if session is not able to handle all that packets
			 */
		} else if (mars_header.type == TDS_SMP_FIN) {
			if (size != sizeof(mars_header))
				goto Severe_Error;
			/* this socket shold now not start another session */
//			tds_set_state(tds, TDS_DEAD);
//			tds->sid = -1;
		} else
			goto Severe_Error;

		if (mars_header.type != TDS_SMP_DATA)
			return;
		if (packet->len < size) {
			packet = tds_realloc_packet(packet, size);
			if (!packet) goto Memory_Error;
			conn->recv_packet = packet;
		}
		packet->len = size;
		return;
	}
	assert(conn->recv_pos <= packet->len && packet->len <= packet->capacity);

	/* normal packet */
	if (conn->recv_pos >= 8) {
		len = TDS_GET_A2BE(&packet->buf[2]);
		if (len < 8)
			goto Severe_Error;
		if (packet->len < len) {
			packet = tds_realloc_packet(packet, len);
			if (!packet) goto Memory_Error;
			conn->recv_packet = packet;
		}
		packet->len = len;
	}
	return;

Memory_Error:
Severe_Error:
	tds_connection_close(conn);
	tds_free_packets(packet);
	conn->recv_packet = NULL;
}
Пример #7
0
static TDSRET
tds_gss_continue(TDSSOCKET * tds, struct tds_gss_auth *auth, gss_buffer_desc *token_ptr)
{
	gss_buffer_desc send_tok;
	OM_uint32 maj_stat, min_stat = 0;
	OM_uint32 ret_flags;
	int gssapi_flags;
	const char *msg = "???";
	gss_OID pmech = GSS_C_NULL_OID;

	auth->last_stat = GSS_S_COMPLETE;

	send_tok.value = NULL;
	send_tok.length = 0;

	/*
	 * Perform the context-establishement loop.
	 *
	 * On each pass through the loop, token_ptr points to the token
	 * to send to the server (or GSS_C_NO_BUFFER on the first pass).
	 * Every generated token is stored in send_tok which is then
	 * transmitted to the server; every received token is stored in
	 * recv_tok, which token_ptr is then set to, to be processed by
	 * the next call to gss_init_sec_context.
	 * 
	 * GSS-API guarantees that send_tok's length will be non-zero
	 * if and only if the server is expecting another token from us,
	 * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
	 * and only if the server has another token to send us.
	 */
	
	/* 
	 * We always want to ask for the mutual, replay, and integ flags.  
	 * We may ask for delegation based on config in the tds.conf and other conf files.  
	 */
	gssapi_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG;
	
	if (tds->login->gssapi_use_delegation)
		gssapi_flags |= GSS_C_DELEG_FLAG;

	maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &auth->gss_context, auth->target_name, 
					GSS_C_NULL_OID,
					gssapi_flags,
					0, NULL,	/* no channel bindings */
					token_ptr, 
					&pmech,	
					&send_tok, &ret_flags, NULL);	/* ignore time_rec */

	tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: actual mechanism at 0x%p\n", pmech);
	if (pmech && pmech->elements) {
		tdsdump_dump_buf(TDS_DBG_NETWORK, "actual mechanism", pmech->elements, pmech->length);
	}
	
	auth->last_stat = maj_stat;
	
	switch (maj_stat) {
	case GSS_S_COMPLETE: 
		msg = "GSS_S_COMPLETE: gss_init_sec_context completed successfully.";
		break;
	case GSS_S_CONTINUE_NEEDED: 
		msg = "GSS_S_CONTINUE_NEEDED: gss_init_sec_context() routine must be called again.";
		break;
	case GSS_S_FAILURE: 
		msg = "GSS_S_FAILURE: The routine failed for reasons that are not defined at the GSS level.";
		tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: min_stat %ld \"%s\"\n", 
						(long) min_stat, error_message(min_stat));
		break;
	case GSS_S_BAD_BINDINGS: 
		msg = "GSS_S_BAD_BINDINGS: The channel bindings are not valid.";
		break;
	case GSS_S_BAD_MECH: 
		msg = "GSS_S_BAD_MECH: The request security mechanism is not supported.";
		break;
	case GSS_S_BAD_NAME: 
		msg = "GSS_S_BAD_NAME: The target_name parameter is not valid.";
		break;
	case GSS_S_BAD_SIG: 
		msg = "GSS_S_BAD_SIG: The input token contains an incorrect integrity check value.";
		break;
	case GSS_S_CREDENTIALS_EXPIRED: 
		msg = "GSS_S_CREDENTIALS_EXPIRED: The supplied credentials are no longer valid.";
		break;
	case GSS_S_DEFECTIVE_CREDENTIAL: 
		msg = "GSS_S_DEFECTIVE_CREDENTIAL: Consistency checks performed on the credential failed.";
		break;
	case GSS_S_DEFECTIVE_TOKEN: 
		msg = "GSS_S_DEFECTIVE_TOKEN: Consistency checks performed on the input token failed.";
		break;
	case GSS_S_DUPLICATE_TOKEN: 
		msg = "GSS_S_DUPLICATE_TOKEN: The token is a duplicate of a token that has already been processed.";
		break;
	case GSS_S_NO_CONTEXT: 
		msg = "GSS_S_NO_CONTEXT: The context handle provided by the caller does not refer to a valid security context.";
		break;
	case GSS_S_NO_CRED: 
		msg = "GSS_S_NO_CRED: The supplied credential handle does not refer to a valid credential, the supplied credential is not";
		break;
	case GSS_S_OLD_TOKEN: 
		msg = "GSS_S_OLD_TOKEN: The token is too old to be checked for duplication against previous tokens which have already been processed.";
		break;
	}
	
	if (GSS_ERROR(maj_stat)) {
		gss_release_buffer(&min_stat, &send_tok);
		tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: %s\n", msg);
		return TDS_FAIL;
	}

	auth->tds_auth.packet = (TDS_UCHAR *) send_tok.value;
	auth->tds_auth.packet_len = send_tok.length;

	return TDS_SUCCESS;
}
Пример #8
0
TDSERRNO
tds_open_socket(TDSSOCKET *tds, struct addrinfo *addr, unsigned int port, int timeout, int *p_oserr)
{
	ioctl_nonblocking_t ioctl_nonblocking;
	SOCKLEN_T optlen;
	TDSCONNECTION *conn = tds->conn;
	char ipaddr[128];
	
	int retval, len;
	TDSERRNO tds_error = TDSECONN;

	*p_oserr = 0;

	tds_addrinfo_set_port(addr, port);
	tds_addrinfo2str(addr, ipaddr, sizeof(ipaddr));

	tdsdump_log(TDS_DBG_INFO1, "Connecting to %s port %d (TDS version %d.%d)\n", 
			ipaddr, port,
			TDS_MAJOR(conn), TDS_MINOR(conn));

	conn->s = socket(addr->ai_family, SOCK_STREAM, 0);
	if (TDS_IS_SOCKET_INVALID(conn->s)) {
		char *errstr = sock_strerror(*p_oserr = sock_errno);
		tdsdump_log(TDS_DBG_ERROR, "socket creation error: %s\n", errstr);
		sock_strerror_free(errstr);
		return TDSESOCK;
	}
	tds->state = TDS_IDLE;

#ifdef SO_KEEPALIVE
	len = 1;
	setsockopt(conn->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len));
#endif

#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL)
	len = 40;
	setsockopt(conn->s, SOL_TCP, TCP_KEEPIDLE, (const void *) &len, sizeof(len));
	len = 2;
	setsockopt(conn->s, SOL_TCP, TCP_KEEPINTVL, (const void *) &len, sizeof(len));
#endif

#if defined(__APPLE__) && defined(SO_NOSIGPIPE)
	len = 1;
	if (setsockopt(conn->s, SOL_SOCKET, SO_NOSIGPIPE, (const void *) &len, sizeof(len))) {
		*p_oserr = sock_errno;
		tds_connection_close(conn);
		return TDSESOCK;
	}
#endif

	len = 1;
#if defined(USE_NODELAY)
	setsockopt(conn->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len));
#elif defined(USE_CORK)
	if (setsockopt(conn->s, SOL_TCP, TCP_CORK, (const void *) &len, sizeof(len)) < 0)
		setsockopt(conn->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len));
#else
#error One should be defined
#endif

#ifdef  DOS32X			/* the other connection doesn't work  on WATTCP32 */
	if (connect(conn->s, addr->ai_addr, addr->ai_addrlen) < 0) {
		char *message;

		*p_oserr = sock_errno;
		if (asprintf(&message, "tds_open_socket(): %s:%d", ipaddr, port) >= 0) {
			perror(message);
			free(message);
		}
		tds_connection_close(conn);
		return TDSECONN;
	}
#else
	if (!timeout) {
		/* A timeout of zero means wait forever; 90,000 seconds will feel like forever. */
		timeout = 90000;
	}

	/* enable non-blocking mode */
	ioctl_nonblocking = 1;
	if (IOCTLSOCKET(conn->s, FIONBIO, &ioctl_nonblocking) < 0) {
		*p_oserr = sock_errno;
		tds_connection_close(conn);
		return TDSEUSCT; 	/* close enough: "Unable to set communications timer" */
	}
	retval = connect(conn->s, addr->ai_addr, addr->ai_addrlen);
	if (retval == 0) {
		tdsdump_log(TDS_DBG_INFO2, "connection established\n");
	} else {
		int err = *p_oserr = sock_errno;
		char *errstr = sock_strerror(err);
		tdsdump_log(TDS_DBG_ERROR, "tds_open_socket: connect(2) returned \"%s\"\n", errstr);
		sock_strerror_free(errstr);
#if DEBUGGING_CONNECTING_PROBLEM
		if (err != ECONNREFUSED && err != ENETUNREACH && err != TDSSOCK_EINPROGRESS) {
			tdsdump_dump_buf(TDS_DBG_ERROR, "Contents of sockaddr_in", addr->ai_addr, addr->ai_addrlen);
			tdsdump_log(TDS_DBG_ERROR, 	" sockaddr_in:\t"
							      "%s = %x\n" 
							"\t\t\t%s = %x\n" 
							"\t\t\t%s = %s\n"
							, "sin_family", addr->ai_family
							, "port", port
							, "address", ipaddr
							);
		}
#endif
		if (err != TDSSOCK_EINPROGRESS)
			goto not_available;
		
		*p_oserr = TDSSOCK_ETIMEDOUT;
		if (tds_select(tds, TDSSELWRITE|TDSSELERR, timeout) == 0) {
			tds_error = TDSECONN;
			goto not_available;
		}
	}
#endif	/* not DOS32X */

	/* check socket error */
	optlen = sizeof(len);
	len = 0;
	if (tds_getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (char *) &len, &optlen) != 0) {
		char *errstr = sock_strerror(*p_oserr = sock_errno);
		tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) failed: %s\n", errstr);
		sock_strerror_free(errstr);
		goto not_available;
	}
	if (len != 0) {
		char *errstr = sock_strerror(*p_oserr = len);
		tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) reported: %s\n", errstr);
		sock_strerror_free(errstr);
		goto not_available;
	}

	tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() succeeded\n");
	return TDSEOK;
	
    not_available:
	
	tds_connection_close(conn);
	tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() failed\n");
	return tds_error;
}
Пример #9
0
/**
 * Read in one 'packet' from the server.  This is a wrapped outer packet of
 * the protocol (they bundle result packets into chunks and wrap them at
 * what appears to be 512 bytes regardless of how that breaks internal packet
 * up.   (tetherow\@nol.org)
 * @return bytes read or -1 on failure
 */
int
tds_read_packet(TDSSOCKET * tds)
{
	unsigned char header[8];
	int len;
	int x = 0, have, need;

	if (IS_TDSDEAD(tds)) {
		tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD");
		return -1;
	}

	/*
	 * Read in the packet header.  We use this to figure out our packet
	 * length
	 */

	/*
	 * Cast to int are needed because some compiler seem to convert
	 * len to unsigned (as FreeBSD 4.5 one)
	 */
	if ((len = goodread(tds, header, sizeof(header))) < (int) sizeof(header)) {
		/* GW ADDED */
		if (len < 0) {
			tds_client_msg(tds->tds_ctx, tds, 20004, 6, 0, 0, "Read from SQL server failed.");
			tds_close_socket(tds);
			tds->in_len = 0;
			tds->in_pos = 0;
			return -1;
		}

		/* GW ADDED */
		/*
		 * Not sure if this is the best way to do the error
		 * handling here but this is the way it is currently
		 * being done.
		 */

		tds->in_len = 0;
		tds->in_pos = 0;
		tds->last_packet = 1;
		if (tds->state != TDS_IDLE && len == 0) {
			tds_close_socket(tds);
		}
		return -1;
	}
	tdsdump_dump_buf(TDS_DBG_NETWORK, "Received header", header, sizeof(header));

#if 0
	/*
	 * Note:
	 * this was done by Gregg, I don't think its the real solution (it breaks
	 * under 5.0, but I haven't gotten a result big enough to test this yet.
 	 */
	if (IS_TDS42(tds)) {
		if (header[0] != 0x04 && header[0] != 0x0f) {
			tdsdump_log(TDS_DBG_ERROR, "Invalid packet header %d\n", header[0]);
			/*
			 * Not sure if this is the best way to do the error
			 * handling here but this is the way it is currently
			 * being done.
			 */
			tds->in_len = 0;
			tds->in_pos = 0;
			tds->last_packet = 1;
			return (-1);
		}
	}
#endif

	/* Convert our packet length from network to host byte order */
	len = ((((unsigned int) header[2]) << 8) | header[3]) - 8;
	need = len;

	/*
	 * If this packet size is the largest we have gotten allocate
	 * space for it
	 */
	if ((unsigned int)len > tds->in_buf_max) {
		unsigned char *p;

		if (!tds->in_buf) {
			p = (unsigned char *) malloc(len);
		} else {
			p = (unsigned char *) realloc(tds->in_buf, len);
		}
		if (!p)
			return -1;	/* FIXME should close socket too */
		tds->in_buf = p;
		/* Set the new maximum packet size */
		tds->in_buf_max = len;
	}

	/* Clean out the in_buf so we don't use old stuff by mistake */
	memset(tds->in_buf, 0, tds->in_buf_max);

	/* Now get exactly how many bytes the server told us to get */
	have = 0;
	while (need > 0) {
		if ((x = goodread(tds, tds->in_buf + have, need)) < 1) {
			/*
			 * Not sure if this is the best way to do the error
			 * handling here but this is the way it is currently
			 * being done.
			 */
			tds->in_len = 0;
			tds->in_pos = 0;
			tds->last_packet = 1;
			/* FIXME should this be "if (x == 0)" ? */
			if (len == 0) {
				tds_close_socket(tds);
			}
			return (-1);
		}
		have += x;
		need -= x;
	}
	if (x < 1) {
		/*
		 * Not sure if this is the best way to do the error handling
		 * here but this is the way it is currently being done.
		 */
		tds->in_len = 0;
		tds->in_pos = 0;
		tds->last_packet = 1;
		/* return 0 if header found but no payload */
		return len ? -1 : 0;
	}

	/* Set the last packet flag */
	if (header[1]) {
		tds->last_packet = 1;
	} else {
		tds->last_packet = 0;
	}

	/* set the received packet type flag */
	tds->in_flag = header[0];

	/* Set the length and pos (not sure what pos is used for now */
	tds->in_len = have;
	tds->in_pos = 0;
	tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len);

	return (tds->in_len);
}
Пример #10
0
static TDSERRNO
tds_connect_socket(TDSSOCKET *tds, struct addrinfo *addr, unsigned int port, int timeout, int *p_oserr)
{
	SOCKLEN_T optlen;
	TDSCONNECTION *conn = tds->conn;
	char ipaddr[128];

	int retval, len;

	tds_addrinfo_set_port(addr, port);
	tds_addrinfo2str(addr, ipaddr, sizeof(ipaddr));

	if (TDS_IS_SOCKET_INVALID(conn->s))
		return TDSECONN;

	*p_oserr = 0;

	tdsdump_log(TDS_DBG_INFO1, "Connecting to %s port %d (TDS version %d.%d)\n", 
			ipaddr, port,
			TDS_MAJOR(conn), TDS_MINOR(conn));

#ifdef  DOS32X			/* the other connection doesn't work  on WATTCP32 */
	if (connect(conn->s, addr->ai_addr, addr->ai_addrlen) < 0) {
		*p_oserr = sock_errno;
		tdsdump_log(TDS_DBG_ERROR, "tds_open_socket(): %s:%d", ipaddr, port);
		return TDSECONN;
	}
#else
	if (!timeout) {
		/* A timeout of zero means wait forever; 90,000 seconds will feel like forever. */
		timeout = 90000;
	}

	if ((*p_oserr = tds_socket_set_nonblocking(conn->s)) != 0) {
		tds_connection_close(conn);
		return TDSEUSCT; 	/* close enough: "Unable to set communications timer" */
	}
	retval = connect(conn->s, addr->ai_addr, addr->ai_addrlen);
	if (retval == 0) {
		tdsdump_log(TDS_DBG_INFO2, "connection established\n");
	} else {
		int err = *p_oserr = sock_errno;
		char *errstr = sock_strerror(err);
		tdsdump_log(TDS_DBG_ERROR, "tds_open_socket: connect(2) returned \"%s\"\n", errstr);
		sock_strerror_free(errstr);
#if DEBUGGING_CONNECTING_PROBLEM
		if (err != ECONNREFUSED && err != ENETUNREACH && err != TDSSOCK_EINPROGRESS) {
			tdsdump_dump_buf(TDS_DBG_ERROR, "Contents of sockaddr_in", addr->ai_addr, addr->ai_addrlen);
			tdsdump_log(TDS_DBG_ERROR, 	" sockaddr_in:\t"
							      "%s = %x\n" 
							"\t\t\t%s = %x\n" 
							"\t\t\t%s = %s\n"
							, "sin_family", addr->ai_family
							, "port", port
							, "address", ipaddr
							);
		}
#endif
		if (err != TDSSOCK_EINPROGRESS)
			return TDSECONN;
		
		*p_oserr = TDSSOCK_ETIMEDOUT;
		if (tds_select(tds, TDSSELWRITE|TDSSELERR, timeout) == 0)
			return TDSECONN;
	}
#endif	/* not DOS32X */

	/* check socket error */
	optlen = sizeof(len);
	len = 0;
	if (tds_getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (char *) &len, &optlen) != 0) {
		char *errstr = sock_strerror(*p_oserr = sock_errno);
		tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) failed: %s\n", errstr);
		sock_strerror_free(errstr);
		return TDSECONN;
	}
	if (len != 0) {
		char *errstr = sock_strerror(*p_oserr = len);
		tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) reported: %s\n", errstr);
		sock_strerror_free(errstr);
		return TDSECONN;
	}

	return TDSEOK;
}