static int negotiate_proxy(int fd, const char *hostname, int port) { char buf[MY_BUFLEN]; char *bp; int ok = 0; /* * version negotiation (converse to pmproxy logic) * __pmSend my client version message * __pmRecv server version message * __pmSend hostname and port */ if (__pmSend(fd, MY_VERSION, strlen(MY_VERSION), 0) != strlen(MY_VERSION)) { char errmsg[PM_MAXERRMSGLEN]; __pmNotifyErr(LOG_WARNING, "__pmConnectPMCD: send version string to pmproxy failed: %s\n", pmErrStr_r(-neterror(), errmsg, sizeof(errmsg))); return PM_ERR_IPC; } for (bp = buf; bp < &buf[MY_BUFLEN]; bp++) { if (__pmRecv(fd, bp, 1, 0) != 1) { *bp = '\0'; bp = &buf[MY_BUFLEN]; break; } if (*bp == '\n' || *bp == '\r') { *bp = '\0'; break; } } if (bp < &buf[MY_BUFLEN]) { if (strcmp(buf, "pmproxy-server 1") == 0) ok = 1; } if (!ok) { __pmNotifyErr(LOG_WARNING, "__pmConnectPMCD: bad version string from pmproxy: \"%s\"\n", buf); return PM_ERR_IPC; } snprintf(buf, sizeof(buf), "%s %d\n", hostname, port); if (__pmSend(fd, buf, strlen(buf), 0) != strlen(buf)) { char errmsg[PM_MAXERRMSGLEN]; __pmNotifyErr(LOG_WARNING, "__pmConnectPMCD: send hostname+port string to pmproxy failed: %s'\n", pmErrStr_r(-neterror(), errmsg, sizeof(errmsg))); return PM_ERR_IPC; } return ok; }
static int http_client_response(http_client *cp) { size_t bytes; char buffer[BUFSIZ]; int sts; static int setup; static http_parser_settings settings; if (!setup) { memset(&settings, 0, sizeof(settings)); settings.on_header_field = on_header_field; settings.on_header_value = on_header_value; settings.on_headers_complete = on_headers_complete; settings.on_body = on_body; settings.on_message_complete = on_message_complete; setup = 1; } if (pmDebug & DBG_TRACE_HTTP) fprintf(stderr, "http_client_response\n"); http_parser_init(&cp->parser, HTTP_RESPONSE); cp->parser.data = (void *)cp; cp->error_code = 0; cp->offset = 0; do { if ((sts = __pmRecv(cp->fd, buffer, sizeof(buffer), 0)) <= 0) { http_client_disconnect(cp); return sts ? sts : -EAGAIN; } bytes = http_parser_execute(&cp->parser, &settings, buffer, sts); } while (bytes && !(cp->flags & F_MESSAGE_END)); if (http_should_client_disconnect(cp)) http_client_disconnect(cp); if (http_should_client_redirect(cp)) return -EMLINK; if (http_should_keep_alive(&cp->parser) == 0) http_client_disconnect(cp); if (cp->error_code) return cp->error_code; return cp->offset; }
static int pduread(int fd, char *buf, int len, int part, int timeout) { int socketipc = __pmSocketIPC(fd); int status = 0; int have = 0; int onetrip = 1; struct timeval dead_hand; struct timeval now; if (timeout == -2 /*TIMEOUT_ASYNC*/) return -EOPNOTSUPP; /* * Handle short reads that may split a PDU ... * * The original logic here assumed that recv() would only split a * PDU at a word (__pmPDU) boundary ... with the introduction of * secure connections, SSL and possibly compression all lurking * below the socket covers, this is no longer a safe assumption. * * So, we keep nibbling at the input stream until we have all that * we have requested, or we timeout, or error. */ while (len) { struct timeval wait; #if defined(IS_MINGW) /* cannot select on a pipe on Win32 - yay! */ if (!__pmSocketIPC(fd)) { COMMTIMEOUTS cwait = { 0 }; if (timeout != TIMEOUT_NEVER) cwait.ReadTotalTimeoutConstant = timeout * 1000.0; else cwait.ReadTotalTimeoutConstant = def_timeout * 1000.0; SetCommTimeouts((HANDLE)_get_osfhandle(fd), &cwait); } else #endif /* * either never timeout (i.e. block forever), or timeout */ if (timeout != TIMEOUT_NEVER) { if (timeout > 0) { wait.tv_sec = timeout; wait.tv_usec = 0; } else wait = def_wait; if (onetrip) { /* * Need all parts of the PDU to be received by dead_hand * This enforces a low overall timeout for the whole PDU * (as opposed to just a timeout for individual calls to * recv). A more invasive alternative (better) approach * would see all I/O performed in the main event loop, * and I/O routines transformed to continuation-passing * style. */ gettimeofday(&dead_hand, NULL); dead_hand.tv_sec += wait.tv_sec; dead_hand.tv_usec += wait.tv_usec; while (dead_hand.tv_usec >= 1000000) { dead_hand.tv_usec -= 1000000; dead_hand.tv_sec++; } onetrip = 0; } status = __pmSocketReady(fd, &wait); if (status > 0) { gettimeofday(&now, NULL); if (now.tv_sec > dead_hand.tv_sec || (now.tv_sec == dead_hand.tv_sec && now.tv_usec > dead_hand.tv_usec)) status = 0; } if (status == 0) { if (__pmGetInternalState() != PM_STATE_APPL) { /* special for PMCD and friends * Note, on Linux select would return 'time remaining' * in timeout value, so report the expected timeout */ int tosec, tomsec; if ( timeout != TIMEOUT_NEVER && timeout > 0 ) { tosec = (int)timeout; tomsec = 0; } else { tosec = (int)def_wait.tv_sec; tomsec = 1000*(int)def_wait.tv_usec; } __pmNotifyErr(LOG_WARNING, "pduread: timeout (after %d.%03d " "sec) while attempting to read %d " "bytes out of %d in %s on fd=%d", tosec, tomsec, len - have, len, part == HEADER ? "HDR" : "BODY", fd); } return PM_ERR_TIMEOUT; } else if (status < 0) { char errmsg[PM_MAXERRMSGLEN]; __pmNotifyErr(LOG_ERR, "pduread: select() on fd=%d: %s", fd, netstrerror_r(errmsg, sizeof(errmsg))); setoserror(neterror()); return status; } } if (socketipc) { status = __pmRecv(fd, buf, len, 0); setoserror(neterror()); } else { status = read(fd, buf, len); } __pmOverrideLastFd(fd); if (status < 0) /* error */ return status; else if (status == 0) /* return what we have, or nothing */ break; have += status; buf += status; len -= status; #ifdef PCP_DEBUG if ((pmDebug & DBG_TRACE_PDU) && (pmDebug & DBG_TRACE_DESPERATE)) { fprintf(stderr, "pduread(%d, ...): have %d, last read %d, still need %d\n", fd, have, status, len); } #endif } return have; }
/* Establish a new socket connection to a client */ ClientInfo * AcceptNewClient(int reqfd) { int i; int fd; __pmSockLen addrlen; int ok = 0; char buf[MY_BUFLEN]; char *bp; char *endp; char *abufp; i = NewClient(); addrlen = __pmSockAddrSize(); fd = __pmAccept(reqfd, client[i].addr, &addrlen); if (fd == -1) { __pmNotifyErr(LOG_ERR, "AcceptNewClient(%d) __pmAccept failed: %s", reqfd, netstrerror()); Shutdown(); exit(1); } __pmSetSocketIPC(fd); if (fd > maxSockFd) maxSockFd = fd; __pmFD_SET(fd, &sockFds); client[i].fd = fd; client[i].pmcd_fd = -1; client[i].status.connected = 1; client[i].status.allowed = 0; client[i].pmcd_hostname = NULL; /* * version negotiation (converse to negotiate_proxy() logic in * libpcp * * __pmRecv client version message * __pmSend my server version message * __pmRecv pmcd hostname and pmcd port */ for (bp = buf; bp < &buf[MY_BUFLEN]; bp++) { if (__pmRecv(fd, bp, 1, 0) != 1) { *bp = '\0'; /* null terminate what we have */ bp = &buf[MY_BUFLEN]; /* flag error */ break; } /* end of line means no more ... */ if (*bp == '\n' || *bp == '\r') { *bp = '\0'; break; } } if (bp < &buf[MY_BUFLEN]) { /* looks OK so far ... is this a version we can support? */ if (strcmp(buf, "pmproxy-client 1") == 0) { client[i].version = 1; ok = 1; } } if (!ok) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { abufp = __pmSockAddrToString(client[i].addr); __pmNotifyErr(LOG_INFO, "Bad version string from client at %s", abufp); free(abufp); fprintf(stderr, "AcceptNewClient: bad version string was \""); for (bp = buf; *bp && bp < &buf[MY_BUFLEN]; bp++) fputc(*bp & 0xff, stderr); fprintf(stderr, "\"\n"); } #endif DeleteClient(&client[i]); return NULL; } if (__pmSend(fd, MY_VERSION, strlen(MY_VERSION), 0) != strlen(MY_VERSION)) { abufp = __pmSockAddrToString(client[i].addr); __pmNotifyErr(LOG_WARNING, "AcceptNewClient: failed to send version " "string (%s) to client at %s\n", MY_VERSION, abufp); free(abufp); DeleteClient(&client[i]); return NULL; } for (bp = buf; bp < &buf[MY_BUFLEN]; bp++) { if (__pmRecv(fd, bp, 1, 0) != 1) { *bp = '\0'; /* null terminate what we have */ bp = &buf[MY_BUFLEN]; /* flag error */ break; } /* end of line means no more ... */ if (*bp == '\n' || *bp == '\r') { *bp = '\0'; break; } } if (bp < &buf[MY_BUFLEN]) { /* looks OK so far ... get hostname and port */ for (bp = buf; *bp && *bp != ' '; bp++) ; if (bp != buf) { *bp = '\0'; client[i].pmcd_hostname = strdup(buf); if (client[i].pmcd_hostname == NULL) __pmNoMem("PMCD.hostname", strlen(buf), PM_FATAL_ERR); bp++; client[i].pmcd_port = (int)strtoul(bp, &endp, 10); if (*endp != '\0') { abufp = __pmSockAddrToString(client[i].addr); __pmNotifyErr(LOG_WARNING, "AcceptNewClient: bad pmcd port " "\"%s\" from client at %s", bp, abufp); free(abufp); DeleteClient(&client[i]); return NULL; } } /* error, fall through */ } if (client[i].pmcd_hostname == NULL) { abufp = __pmSockAddrToString(client[i].addr); __pmNotifyErr(LOG_WARNING, "AcceptNewClient: failed to get PMCD " "hostname (%s) from client at %s", buf, abufp); free(abufp); DeleteClient(&client[i]); return NULL; } #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { /* * note error message gets appended to once pmcd connection is * made in ClientLoop() */ abufp = __pmSockAddrToString(client[i].addr); fprintf(stderr, "AcceptNewClient [%d] fd=%d from %s to %s (port %s)", i, fd, abufp, client[i].pmcd_hostname, bp); free(abufp); } #endif return &client[i]; }