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 + -
显示快捷键?