fe-connect.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,647 行 · 第 1/5 页

C
2,647
字号
	}}/* ---------------- *		PQconnectPoll * * Poll an asynchronous connection. * * Returns a PostgresPollingStatusType. * Before calling this function, use select(2) to determine when data * has arrived.. * * You must call PQfinish whether or not this fails. * * This function and PQconnectStart are intended to allow connections to be * made without blocking the execution of your program on remote I/O. However, * there are a number of caveats: * *	 o	If you call PQtrace, ensure that the stream object into which you trace *		will not block. *	 o	If you do not supply an IP address for the remote host (i.e. you *		supply a host name instead) then PQconnectStart will block on *		gethostbyname.	You will be fine if using Unix sockets (i.e. by *		supplying neither a host name nor a host address). *	 o	If your backend wants to use Kerberos authentication then you must *		supply both a host name and a host address, otherwise this function *		may block on gethostname. * * ---------------- */PostgresPollingStatusTypePQconnectPoll(PGconn *conn){	PGresult   *res;	char		sebuf[256];	if (conn == NULL)		return PGRES_POLLING_FAILED;	/* Get the new data */	switch (conn->status)	{			/*			 * We really shouldn't have been polled in these two cases,			 * but we can handle it.			 */		case CONNECTION_BAD:			return PGRES_POLLING_FAILED;		case CONNECTION_OK:			return PGRES_POLLING_OK;			/* These are reading states */		case CONNECTION_AWAITING_RESPONSE:		case CONNECTION_AUTH_OK:			{				/* Load waiting data */				int			n = pqReadData(conn);				if (n < 0)					goto error_return;				if (n == 0)					return PGRES_POLLING_READING;				break;			}			/* These are writing states, so we just proceed. */		case CONNECTION_STARTED:		case CONNECTION_MADE:			break;			/* We allow pqSetenvPoll to decide whether to proceed. */		case CONNECTION_SETENV:			break;			/* Special cases: proceed without waiting. */		case CONNECTION_SSL_STARTUP:		case CONNECTION_NEEDED:			break;		default:			printfPQExpBuffer(&conn->errorMessage,							  libpq_gettext(											"invalid connection state, "							 "probably indicative of memory corruption\n"											));			goto error_return;	}keep_going:						/* We will come back to here until there								 * is nothing left to do. */	switch (conn->status)	{		case CONNECTION_NEEDED:			{				/*				 * Try to initiate a connection to one of the addresses				 * returned by getaddrinfo_all().  conn->addr_cur is the				 * next one to try.  We fail when we run out of addresses				 * (reporting the error returned for the *last*				 * alternative, which may not be what users expect :-().				 */				while (conn->addr_cur != NULL)				{					struct addrinfo *addr_cur = conn->addr_cur;					/* Remember current address for possible error msg */					memcpy(&conn->raddr.addr, addr_cur->ai_addr,						   addr_cur->ai_addrlen);					conn->raddr.salen = addr_cur->ai_addrlen;					/* Open a socket */					conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);					if (conn->sock < 0)					{						/*						 * ignore socket() failure if we have more						 * addresses to try						 */						if (addr_cur->ai_next != NULL)						{							conn->addr_cur = addr_cur->ai_next;							continue;						}						printfPQExpBuffer(&conn->errorMessage,						  libpq_gettext("could not create socket: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));						break;					}					/*					 * Select socket options: no delay of outgoing data					 * for TCP sockets, and nonblock mode.	Fail if this					 * fails.					 */					if (!IS_AF_UNIX(addr_cur->ai_family))					{						if (!connectNoDelay(conn))						{							closesocket(conn->sock);							conn->sock = -1;							conn->addr_cur = addr_cur->ai_next;							continue;						}					}					if (connectMakeNonblocking(conn) == 0)					{						closesocket(conn->sock);						conn->sock = -1;						conn->addr_cur = addr_cur->ai_next;						continue;					}					/*					 * Start/make connection.  This should not block,					 * since we are in nonblock mode.  If it does, well,					 * too bad.					 */			retry_connect:					if (connect(conn->sock, addr_cur->ai_addr,								addr_cur->ai_addrlen) < 0)					{						if (SOCK_ERRNO == EINTR)							/* Interrupted system call - just try again */							goto retry_connect;						if (SOCK_ERRNO == EINPROGRESS ||							SOCK_ERRNO == EWOULDBLOCK ||							SOCK_ERRNO == 0)						{							/*							 * This is fine - we're in non-blocking mode,							 * and the connection is in progress.  Tell							 * caller to wait for write-ready on socket.							 */							conn->status = CONNECTION_STARTED;							return PGRES_POLLING_WRITING;						}						/* otherwise, trouble */					}					else					{						/*						 * Hm, we're connected already --- seems the						 * "nonblock connection" wasn't.  Advance the						 * state machine and go do the next stuff.						 */						conn->status = CONNECTION_STARTED;						goto keep_going;					}					/*					 * This connection failed --- set up error report,					 * then close socket (do it this way in case close()					 * affects the value of errno...).	We will ignore the					 * connect() failure and keep going if there are more					 * addresses.					 */					connectFailureMessage(conn, SOCK_ERRNO);					if (conn->sock >= 0)					{						closesocket(conn->sock);						conn->sock = -1;					}					/*					 * Try the next address, if any.					 */					conn->addr_cur = addr_cur->ai_next;				}				/* loop over addresses */				/*				 * Ooops, no more addresses.  An appropriate error message				 * is already set up, so just set the right status.				 */				goto error_return;			}		case CONNECTION_STARTED:			{				int			optval;				ACCEPT_TYPE_ARG3 optlen = sizeof(optval);				/*				 * Write ready, since we've made it here, so the				 * connection has been made ... or has failed.				 */				/*				 * Now check (using getsockopt) that there is not an error				 * state waiting for us on the socket.				 */				if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,							   (char *) &optval, &optlen) == -1)				{					printfPQExpBuffer(&conn->errorMessage,									  libpq_gettext("could not get socket error status: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));					goto error_return;				}				else if (optval != 0)				{					/*					 * When using a nonblocking connect, we will typically					 * see connect failures at this point, so provide a					 * friendly error message.					 */					connectFailureMessage(conn, optval);					/*					 * If more addresses remain, keep trying, just as in					 * the case where connect() returned failure					 * immediately.					 */					if (conn->addr_cur->ai_next != NULL)					{						if (conn->sock >= 0)						{							closesocket(conn->sock);							conn->sock = -1;						}						conn->addr_cur = conn->addr_cur->ai_next;						conn->status = CONNECTION_NEEDED;						goto keep_going;					}					goto error_return;				}				/* Fill in the client address */				conn->laddr.salen = sizeof(conn->laddr.addr);				if (getsockname(conn->sock,								(struct sockaddr *) & conn->laddr.addr,								&conn->laddr.salen) < 0)				{					printfPQExpBuffer(&conn->errorMessage,									  libpq_gettext("could not get client address from socket: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));					goto error_return;				}				/*				 * Make sure we can write before advancing to next step.				 */				conn->status = CONNECTION_MADE;				return PGRES_POLLING_WRITING;			}		case CONNECTION_MADE:			{				char	   *startpacket;				int			packetlen;#ifdef USE_SSL				/*				 * If SSL is enabled and we haven't already got it				 * running, request it instead of sending the startup				 * message.				 */				if (IS_AF_UNIX(conn->raddr.addr.ss_family))				{					/* Don't bother requesting SSL over a Unix socket */					conn->allow_ssl_try = false;				}				if (conn->allow_ssl_try && !conn->wait_ssl_try &&					conn->ssl == NULL)				{					ProtocolVersion pv;					/*					 * Send the SSL request packet.					 *					 * Theoretically, this could block, but it really					 * shouldn't since we only got here if the socket is					 * write-ready.					 */					pv = htonl(NEGOTIATE_SSL_CODE);					if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)					{						printfPQExpBuffer(&conn->errorMessage,										  libpq_gettext("could not send SSL negotiation packet: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));						goto error_return;					}					/* Ok, wait for response */					conn->status = CONNECTION_SSL_STARTUP;					return PGRES_POLLING_READING;				}#endif   /* USE_SSL */				/*				 * Build the startup packet.				 */				if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)					startpacket = pqBuildStartupPacket3(conn, &packetlen,													 EnvironmentOptions);				else					startpacket = pqBuildStartupPacket2(conn, &packetlen,													 EnvironmentOptions);				if (!startpacket)				{					printfPQExpBuffer(&conn->errorMessage,									  libpq_gettext("out of memory\n"));					goto error_return;				}				/*				 * Send the startup packet.				 *				 * Theoretically, this could block, but it really shouldn't				 * since we only got here if the socket is write-ready.				 */				if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)				{					printfPQExpBuffer(&conn->errorMessage,					libpq_gettext("could not send startup packet: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));					free(startpacket);					goto error_return;				}				free(startpacket);				conn->status = CONNECTION_AWAITING_RESPONSE;				return PGRES_POLLING_READING;			}			/*			 * Handle SSL negotiation: wait for postmaster messages and			 * respond as necessary.			 */		case CONNECTION_SSL_STARTUP:			{#ifdef USE_SSL				PostgresPollingStatusType pollres;				/*				 * On first time through, get the postmaster's response to				 * our SSL negotiation packet.	Be careful to read only				 * one byte (if there's more, it could be SSL data).				 */				if (conn->ssl == NULL)				{					char		SSLok;					int			nread;			retry_ssl_read:					nread = recv(conn->sock, &SSLok, 1, 0);					if (nread < 0)					{						if (SOCK_ERRNO == EINTR)							/* Interrupted system call - just try again */							goto retry_ssl_read;						printfPQExpBuffer(&conn->errorMessage,										  libpq_gettext("could not receive server response to SSL negotiation packet: %s\n"),						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));						goto error_return;					}					if (nread == 0)						/* caller failed to wait for data */						return PGRES_POLLING_READING;					if (SSLok == 'S')					{						/* Do one-time setup; this creates conn->ssl */						if (pqsecure_initialize(conn) == -1)							goto error_return;					}					else if (SSLok == 'N')					{						if (conn->sslmode[0] == 'r')	/* "require" */						{							/* Require SSL, but server does not want it */							printfPQExpBuffer(&conn->errorMessage,											  libpq_gettext("server does not support SSL, but SSL was required\n"));							goto error_return;						}						/* Otherwise, proceed with normal startup */						conn->allow_ssl_try = false;						conn->status = CONNECTION_MADE;						return PGRES_POLLING_WRITING;					}					else if (SSLok == 'E')					{						/* Received error - probably protocol mismatch */						if (conn->Pfdebug)							fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-7.0.\n");						if (conn->sslmode[0] == 'r')	/* "require" */						{							/* Require SSL, but server is too old */							printfPQExpBuffer(&conn->errorMessage,											  libpq_gettext("server does not support SSL, but SSL was required\n"));							goto error_return;						}						/* Otherwise, try again without SSL */						conn->allow_ssl_try = false;						/* Assume it ain't gonna handle protocol 3, either */						conn->pversion = PG_PROTOCOL(2, 0);						/* Must drop the old connection */						closesocket(conn->sock);						conn->sock = -1;						conn->status = CONNECTION_NEEDED;						goto keep_going;					}					else					{						printfPQExpBuffer(&conn->errorMessage,										  libpq_gettext("received invalid response to SSL negotiation: %c\n"),										  SSLok);						goto error_return;					}				}				/*				 * Begin or continue the SSL negotiation process.				 */				pollres = pqsecure_open_client(conn);				if (pollres == PGRES_POLLING_OK)				{					/* SSL handshake done, ready to send startup packet */					conn->status = CONNECTION_MADE;					return PGRES_POLLING_WRITING;				}				return pollres;#else							/* !USE_SSL */				/* can't get here */				goto error_return;#endif   /* USE_SSL */			}			/*			 * Handle authentication exchange: wait for postmaster			 * messages and respond as necessary.			 */		case CONNECTION_AWAITING_RESPONSE:			{				char		beresp;				int			msgLength;				int			avail;				AuthRequest areq;				/*				 * Scan the message from current point (note that if we				 * find the message is incomplete, we will return without				 * advancing inStart, and resume here next time).				 */				conn->inCursor = conn->inStart;				/* Read type byte */				if (pqGetc(&beresp, conn))				{					/* We'll come back when there is more data */					return PGRES_POLLING_READING;				}				/*				 * Validate message type: we expect only an authentication				 * request or an error here.  Anything else probably means				 * it's not Postgres on the other end at all.				 */				if (!(beresp == 'R' || beresp == 'E'))				{					printfPQExpBuffer(&conn->errorMessage,									  libpq_gettext(								  "expected authentication request from "											"server, but received %c\n"),									  beresp);					goto error_return;				}				if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)				{					/* Read message length word */					if (pqGetInt(&msgLength, 4, conn))					{						/* We'll come back when there is more data */						return PGRES_POLLING_READING;					}				}				else				{					/* Set phony message length to disable checks below */					msgLength = 8;				}				/*				 * Try to validate message length before using it.				 * Authentication requests can't be very large.  Errors				 * can be a little larger, but not huge.  If we see a

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?