/* * Create socket for incoming connections and bind to it an address for * clients to use. Returns -1 on failure. * * If '*family' is AF_UNIX and unix domain sockets are supported: * 'port' is ignored and 'address' is the path to the socket file in the filesystem. * * Otherwise: * address is a string representing the Inet/IPv6 address that the port * is advertised for. To allow connections to all this host's internet * addresses from clients use address == "INADDR_ANY", or for localhost * access only use address == "INADDR_LOOPBACK". * On input, 'family' is a pointer to the address family to use (AF_INET, * AF_INET6) if the address specified is empty. If the spec is not * empty then family is ignored and is set to the actual address family * used. 'family' must be initialized to AF_UNSPEC, in this case. */ static int OpenRequestSocket(int port, const char *address, int *family, int backlog, __pmFdSet *fdset, int *maximum) { int fd = -1; int one, sts; __pmSockAddr *myAddr; int isUnix = 0; /* * Using this flag will eliminate the need for more conditional * compilation below, hopefully making the code easier to read and maintain. */ #if defined(HAVE_STRUCT_SOCKADDR_UN) if (*family == AF_UNIX) isUnix = 1; #endif if (isUnix) { if ((myAddr = __pmSockAddrAlloc()) == NULL) { __pmNoMem("OpenRequestSocket: can't allocate socket address", sizeof(*myAddr), PM_FATAL_ERR); } /* Initialize the address. */ __pmSockAddrSetFamily(myAddr, *family); __pmSockAddrSetPath(myAddr, address); /* Create the socket. */ fd = __pmCreateUnixSocket(); } else { /* * If the address is unspecified, then use the address family we * have been given, otherwise the family will be determined by * __pmStringToSockAddr. */ if (address == NULL || strcmp(address, "INADDR_ANY") == 0) { if ((myAddr = __pmSockAddrAlloc()) == NULL) { __pmNoMem("OpenRequestSocket: can't allocate socket address", sizeof(*myAddr), PM_FATAL_ERR); } __pmSockAddrInit(myAddr, *family, INADDR_ANY, 0); } else if (strcmp(address, "INADDR_LOOPBACK") == 0) { if ((myAddr = __pmSockAddrAlloc()) == NULL) { __pmNoMem("OpenRequestSocket: can't allocate socket address", sizeof(*myAddr), PM_FATAL_ERR); } __pmSockAddrInit(myAddr, *family, INADDR_LOOPBACK, 0); } else { if ((myAddr = __pmStringToSockAddr(address)) == NULL) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s) invalid address\n", port, address); goto fail; } *family = __pmSockAddrGetFamily(myAddr); } __pmSockAddrSetPort(myAddr, port); /* Create the socket. */ if (*family == AF_INET) fd = __pmCreateSocket(); else if (*family == AF_INET6) fd = __pmCreateIPv6Socket(); else { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s) invalid address family: %d\n", port, address, *family); goto fail; } } if (fd < 0) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) __pmCreateSocket: %s\n", port, address, AddressFamily(*family), netstrerror()); goto fail; } /* Ignore dead client connections. */ one = 1; #ifndef IS_MINGW if (__pmSetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, (__pmSockLen)sizeof(one)) < 0) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) __pmSetSockOpt(SO_REUSEADDR): %s\n", port, address, AddressFamily(*family), netstrerror()); goto fail; } #else if (__pmSetSockOpt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&one, (__pmSockLen)sizeof(one)) < 0) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) __pmSetSockOpt(EXCLUSIVEADDRUSE): %s\n", port, address, AddressFamily(*family), netstrerror()); goto fail; } #endif /* and keep alive please - bad networks eat fds */ if (__pmSetSockOpt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, (__pmSockLen)sizeof(one)) < 0) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) __pmSetSockOpt(SO_KEEPALIVE): %s\n", port, address, AddressFamily(*family), netstrerror()); goto fail; } sts = __pmBind(fd, (void *)myAddr, __pmSockAddrSize()); __pmSockAddrFree(myAddr); myAddr = NULL; if (sts < 0) { sts = neterror(); __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) __pmBind: %s\n", port, address, AddressFamily(*family), netstrerror()); if (sts == EADDRINUSE) __pmNotifyErr(LOG_ERR, "%s may already be running\n", pmProgname); goto fail; } if (isUnix) { /* * For unix domain sockets, grant rw access to the socket for all, * otherwise, on linux platforms, connection will not be possible. * This must be done AFTER binding the address. See Unix(7) for details. */ sts = chmod(address, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (sts != 0) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) chmod(%s): %s\n", port, address, AddressFamily(*family), address, strerror(errno)); goto fail; } } sts = __pmListen(fd, backlog); /* Max. pending connection requests */ if (sts < 0) { __pmNotifyErr(LOG_ERR, "OpenRequestSocket(%d, %s, %s) __pmListen: %s\n", port, address, AddressFamily(*family), netstrerror()); goto fail; } if (fd > *maximum) *maximum = fd; __pmFD_SET(fd, fdset); return fd; fail: if (fd != -1) { __pmCloseSocket(fd); /* We must unlink the socket file. */ if (isUnix) unlink(address); } if (myAddr) __pmSockAddrFree(myAddr); return -1; }
int NewClient(void) { int i, sz; for (i = 0; i < nClients; i++) if (!client[i].status.connected) break; if (i == clientSize) { clientSize = clientSize ? clientSize * 2 : MIN_CLIENTS_ALLOC; sz = sizeof(ClientInfo) * clientSize; client = (ClientInfo *) realloc(client, sz); if (client == NULL) { __pmNoMem("NewClient", sz, PM_RECOV_ERR); Shutdown(); exit(1); } sz -= (sizeof(ClientInfo) * i); memset(&client[i], 0, sz); } client[i].addr = __pmSockAddrAlloc(); if (client[i].addr == NULL) { __pmNoMem("NewClient", __pmSockAddrSize(), PM_RECOV_ERR); Shutdown(); exit(1); } if (i >= nClients) nClients = i + 1; return i; }
/* Establish a new socket connection to a client */ ClientInfo * AcceptNewClient(int reqfd) { static unsigned int seq = 0; int i, fd; __pmSockLen addrlen; struct timeval now; i = NewClient(); addrlen = __pmSockAddrSize(); fd = __pmAccept(reqfd, client[i].addr, &addrlen); if (fd == -1) { if (neterror() == EPERM) { __pmNotifyErr(LOG_NOTICE, "AcceptNewClient(%d): " "Permission Denied\n", reqfd); client[i].fd = -1; DeleteClient(&client[i]); return NULL; } else { __pmNotifyErr(LOG_ERR, "AcceptNewClient(%d) __pmAccept: %s\n", reqfd, netstrerror()); Shutdown(); exit(1); } } if (fd > maxClientFd) maxClientFd = fd; pmcd_openfds_sethi(fd); __pmFD_SET(fd, &clientFds); __pmSetVersionIPC(fd, UNKNOWN_VERSION); /* before negotiation */ __pmSetSocketIPC(fd); client[i].fd = fd; client[i].status.connected = 1; client[i].status.changes = 0; memset(&client[i].attrs, 0, sizeof(__pmHashCtl)); /* * Note seq needs to be unique, but we're using a free running counter * and not bothering to check here ... unless we churn through * 4,294,967,296 (2^32) clients while one client remains connected * we won't have a problem */ client[i].seq = seq++; __pmtimevalNow(&now); client[i].start = now.tv_sec; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_APPL0) fprintf(stderr, "AcceptNewClient(%d): client[%d] (fd %d)\n", reqfd, i, fd); #endif pmcd_trace(TR_ADD_CLIENT, i, 0, 0); return &client[i]; }
/* 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]; }