static POOL_CONNECTION_POOL *connect_backend(StartupPacket *sp, POOL_CONNECTION *frontend) { POOL_CONNECTION_POOL *backend; int i; /* connect to the backend */ backend = pool_create_cp(); if (backend == NULL) { pool_send_error_message(frontend, sp->major, "XX000", "connection cache is full", "", "increase max_pool", __FILE__, __LINE__); pool_close(frontend); pool_free_startup_packet(sp); return NULL; } for (i=0;i<NUM_BACKENDS;i++) { if (VALID_BACKEND(i)) { /* set DB node id */ CONNECTION(backend, i)->db_node_id = i; /* mark this is a backend connection */ CONNECTION(backend, i)->isbackend = 1; pool_ssl_negotiate_clientserver(CONNECTION(backend, i)); /* * save startup packet info */ CONNECTION_SLOT(backend, i)->sp = sp; /* send startup packet */ if (send_startup_packet(CONNECTION_SLOT(backend, i)) < 0) { pool_error("do_child: fails to send startup packet to the %d th backend", i); pool_discard_cp(sp->user, sp->database, sp->major); pool_close(frontend); return NULL; } } } /* * do authentication stuff */ if (pool_do_auth(frontend, backend)) { pool_close(frontend); pool_discard_cp(sp->user, sp->database, sp->major); return NULL; } return backend; }
/* * Tell the user the authentication failed. */ static void auth_failed(POOL_CONNECTION *frontend) { bool send_error_to_frontend = true; int messagelen; char *errmessage; messagelen = strlen(frontend->username) + 100; if ((errmessage = (char *)malloc(messagelen+1)) == NULL) { pool_error("auth_failed: malloc failed: %s", strerror(errno)); child_exit(1); } switch (frontend->auth_method) { case uaReject: snprintf(errmessage, messagelen, "authentication with pgpool failed for user \"%s\": host rejected", frontend->username); /* * if uaReject, frontend should have received 'E' and disconnected already. */ send_error_to_frontend = false; break; /* case uaKrb4: */ /* snprintf(errmessage, messagelen, */ /* "Kerberos 4 authentication with pgpool failed for user \"%s\"", */ /* frontend->username); */ /* break; */ /* case uaKrb5: */ /* snprintf(errmessage, messagelen, */ /* "Kerberos 5 authentication with pgpool failed for user \"%s\"", */ /* frontend->username); */ /* break; */ case uaTrust: snprintf(errmessage, messagelen, "\"trust\" authentication with pgpool failed for user \"%s\"", frontend->username); break; /* case uaIdent: */ /* snprintf(errmessage, messagelen, */ /* "Ident authentication with pgpool failed for user \"%s\"", */ /* frontend->username); */ /* break; */ /* case uaMD5: */ /* case uaCrypt: */ /* case uaPassword: */ /* snprintf(errmessage, messagelen, */ /* "password authentication with pgpool failed for user \"%s\"", */ /* frontend->username); */ /* break; */ #ifdef USE_PAM case uaPAM: snprintf(errmessage, messagelen, "PAM authentication with pgpool failed for user \"%s\"", frontend->username); break; #endif /* USE_PAM */ default: snprintf(errmessage, messagelen, "authentication with pgpool failed for user \"%s\": invalid authentication method", frontend->username); break; } pool_error(errmessage); if (send_error_to_frontend) pool_send_error_message(frontend, frontend->protoVersion, "XX000", errmessage, "", "", __FILE__, __LINE__); /* * don't need to free(errmessage). I will just kill myself. */ close_all_backend_connections(); child_exit(2); }
/* * do frontend <-> pgpool authentication based on pool_hba.conf */ void ClientAuthentication(POOL_CONNECTION *frontend) { POOL_STATUS status = POOL_ERROR; if (! hba_getauthmethod(frontend)) { pool_error("missing or erroneous pool_hba.conf file"); pool_send_error_message(frontend, frontend->protoVersion, "XX000", "missing or erroneous pool_hba.conf file", "", "See pgpool log for details.", __FILE__, __LINE__); close_all_backend_connections(); /* * use exit(2) since this is not so fatal. other entries in * pool_hba.conf may be valid, so treat it as reject. */ child_exit(2); } switch (frontend->auth_method) { case uaReject: { /* * This could have come from an explicit "reject" entry in * pool_hba.conf, but more likely it means there was no matching * entry. Take pity on the poor user and issue a helpful * error message. NOTE: this is not a security breach, * because all the info reported here is known at the frontend * and must be assumed known to bad guys. We're merely helping * out the less clueful good guys. */ char hostinfo[NI_MAXHOST]; char *errmessage; int messagelen; getnameinfo_all(&frontend->raddr.addr, frontend->raddr.salen, hostinfo, sizeof(hostinfo), NULL, 0, NI_NUMERICHOST); messagelen = sizeof(hostinfo) + strlen(frontend->username) + strlen(frontend->database) + 80; if ((errmessage = (char *)malloc(messagelen+1)) == NULL) { pool_error("ClientAuthentication: malloc failed: %s", strerror(errno)); child_exit(1); } #ifdef USE_SSL snprintf(errmessage, messagelen+7, /* +7 is for "SSL off" */ "no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s", hostinfo, frontend->username, frontend->database, frontend->ssl ? "SSL on" : "SSL off"); #else snprintf(errmessage, messagelen, "no pool_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"", hostinfo, frontend->username, frontend->database); #endif pool_error(errmessage); pool_send_error_message(frontend, frontend->protoVersion, "XX000", errmessage, "", "", __FILE__, __LINE__); free(errmessage); break; } /* case uaKrb4: */ /* break; */ /* case uaKrb5: */ /* break; */ /* case uaIdent: */ /* break; */ /* case uaMD5: */ /* break; */ /* case uaCrypt: */ /* break; */ /* case uaPassword: */ /* break; */ #ifdef USE_PAM case uaPAM: pam_frontend_kludge = frontend; status = CheckPAMAuth(frontend, frontend->username, ""); break; #endif /* USE_PAM */ case uaTrust: status = POOL_CONTINUE; break; } if (status == POOL_CONTINUE) sendAuthRequest(frontend, AUTH_REQ_OK); else if (status != POOL_CONTINUE) auth_failed(frontend); }
/* * perform accept() and return new fd */ static POOL_CONNECTION *do_accept(int unix_fd, int inet_fd, struct timeval *timeout) { fd_set readmask; int fds; int save_errno; SockAddr saddr; int fd = 0; int afd; int inet = 0; POOL_CONNECTION *cp; #ifdef ACCEPT_PERFORMANCE struct timeval now1, now2; static long atime; static int cnt; #endif struct timeval *timeoutval; struct timeval tv1, tv2, tmback = {0, 0}; set_ps_display("wait for connection request", false); /* Destroy session context for just in case... */ pool_session_context_destroy(); FD_ZERO(&readmask); FD_SET(unix_fd, &readmask); if (inet_fd) FD_SET(inet_fd, &readmask); if (timeout->tv_sec == 0 && timeout->tv_usec == 0) timeoutval = NULL; else { timeoutval = timeout; tmback.tv_sec = timeout->tv_sec; tmback.tv_usec = timeout->tv_usec; gettimeofday(&tv1, NULL); #ifdef DEBUG pool_log("before select = {%d, %d}", timeoutval->tv_sec, timeoutval->tv_usec); pool_log("g:before select = {%d, %d}", tv1.tv_sec, tv1.tv_usec); #endif } fds = select(Max(unix_fd, inet_fd)+1, &readmask, NULL, NULL, timeoutval); save_errno = errno; /* check backend timer is expired */ if (backend_timer_expired) { pool_backend_timer(); backend_timer_expired = 0; } /* * following code fragment computes remaining timeout val in a * portable way. Linux does this automatically but other platforms do not. */ if (timeoutval) { gettimeofday(&tv2, NULL); tmback.tv_usec -= tv2.tv_usec - tv1.tv_usec; tmback.tv_sec -= tv2.tv_sec - tv1.tv_sec; if (tmback.tv_usec < 0) { tmback.tv_sec--; if (tmback.tv_sec < 0) { timeout->tv_sec = 0; timeout->tv_usec = 0; } else { tmback.tv_usec += 1000000; timeout->tv_sec = tmback.tv_sec; timeout->tv_usec = tmback.tv_usec; } } #ifdef DEBUG pool_log("g:after select = {%d, %d}", tv2.tv_sec, tv2.tv_usec); pool_log("after select = {%d, %d}", timeout->tv_sec, timeout->tv_usec); #endif } errno = save_errno; if (fds == -1) { if (errno == EAGAIN || errno == EINTR) return NULL; pool_error("select() failed. reason %s", strerror(errno)); return NULL; } /* timeout */ if (fds == 0) { return NULL; } if (FD_ISSET(unix_fd, &readmask)) { fd = unix_fd; } if (FD_ISSET(inet_fd, &readmask)) { fd = inet_fd; inet++; } /* * Note that some SysV systems do not work here. For those * systems, we need some locking mechanism for the fd. */ memset(&saddr, 0, sizeof(saddr)); saddr.salen = sizeof(saddr.addr); #ifdef ACCEPT_PERFORMANCE gettimeofday(&now1,0); #endif retry_accept: /* wait if recovery is started */ while (*InRecovery == 1) { pause(); } afd = accept(fd, (struct sockaddr *)&saddr.addr, &saddr.salen); save_errno = errno; /* check backend timer is expired */ if (backend_timer_expired) { pool_backend_timer(); backend_timer_expired = 0; } errno = save_errno; if (afd < 0) { if (errno == EINTR && *InRecovery) goto retry_accept; /* * "Resource temporarily unavailable" (EAGAIN or EWOULDBLOCK) * can be silently ignored. And EINTR can be ignored. */ if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) pool_error("accept() failed. reason: %s", strerror(errno)); return NULL; } #ifdef ACCEPT_PERFORMANCE gettimeofday(&now2,0); atime += (now2.tv_sec - now1.tv_sec)*1000000 + (now2.tv_usec - now1.tv_usec); cnt++; if (cnt % 100 == 0) { pool_log("cnt: %d atime: %ld", cnt, atime); } #endif /* reload config file */ if (got_sighup) { pool_get_config(get_config_file_name(), RELOAD_CONFIG); if (pool_config->enable_pool_hba) { load_hba(get_hba_file_name()); if (strcmp("", pool_config->pool_passwd)) pool_reopen_passwd_file(); } if (pool_config->parallel_mode) pool_memset_system_db_info(system_db_info->info); got_sighup = 0; } connection_count_up(); accepted = 1; if (pool_config->parallel_mode) { /* * do not accept new connection if any of DB node or SystemDB is down when operating in * parallel mode */ int i; for (i=0;i<NUM_BACKENDS;i++) { if (BACKEND_INFO(i).backend_status == CON_DOWN || SYSDB_STATUS == CON_DOWN) { StartupPacket *sp; char *msg = "pgpool is not available in parallel query mode"; if (SYSDB_STATUS == CON_DOWN) pool_log("Cannot accept() new connection. SystemDB is down"); else pool_log("Cannot accept() new connection. %d th backend is down", i); if ((cp = pool_open(afd)) == NULL) { close(afd); child_exit(1); } sp = read_startup_packet(cp); if (sp == NULL) { /* failed to read the startup packet. return to the accept() loop */ pool_close(cp); child_exit(1); } pool_debug("do_accept: send error message to frontend"); if (sp->major == PROTO_MAJOR_V3) { char buf[256]; if (SYSDB_STATUS == CON_DOWN) snprintf(buf, sizeof(buf), "SystemDB is down"); else snprintf(buf, sizeof(buf), "%d th backend is down", i); pool_send_error_message(cp, sp->major, "08S01", msg, buf, ((SYSDB_STATUS == CON_DOWN) ? "repair the SystemDB and restart pgpool" : "repair the backend and restart pgpool"), __FILE__, __LINE__); } else { pool_send_error_message(cp, sp->major, 0, msg, "", "", "", 0); } pool_close(cp); child_exit(1); } } } else { /* * do not accept new connection if all DB nodes are down when operating in * non parallel mode */ int i; int found = 0; for (i=0;i<NUM_BACKENDS;i++) { if (VALID_BACKEND(i)) { found = 1; } } if (found == 0) { pool_log("Cannot accept() new connection. all backends are down"); child_exit(1); } } pool_debug("I am %d accept fd %d", getpid(), afd); pool_getnameinfo_all(&saddr, remote_host, remote_port); snprintf(remote_ps_data, sizeof(remote_ps_data), remote_port[0] == '\0' ? "%s" : "%s(%s)", remote_host, remote_port); set_ps_display("accept connection", false); /* log who is connecting */ if (pool_config->log_connections) { pool_log("connection received: host=%s%s%s", remote_host, remote_port[0] ? " port=" : "", remote_port); } /* set NODELAY and KEEPALIVE options if INET connection */ if (inet) { int on = 1; if (setsockopt(afd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) { pool_error("do_accept: setsockopt() failed: %s", strerror(errno)); close(afd); return NULL; } if (setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) { pool_error("do_accept: setsockopt() failed: %s", strerror(errno)); close(afd); return NULL; } } if ((cp = pool_open(afd)) == NULL) { close(afd); return NULL; } /* save ip address for hba */ memcpy(&cp->raddr, &saddr, sizeof(SockAddr)); if (cp->raddr.addr.ss_family == 0) cp->raddr.addr.ss_family = AF_UNIX; return cp; }