static ErrorObj* ldm_clnt_nullproc(CLIENT* const clnt) { struct timeval timeout; ErrorObj* error; log_assert(NULL != clnt); timeout.tv_sec = 25; /* RPC default */ timeout.tv_usec = 0; if (clnt_call(clnt, NULLPROC, xdr_void, NULL, xdr_void, NULL, timeout) == 0) { error = NULL; } else { struct rpc_err rpcErr; clnt_geterr(clnt, &rpcErr); error = ERR_NEW(rpcErr.re_status, NULL, clnt_errmsg(clnt)); } return error; }
/* * Returns a new upstream filter. * * Arguments: * upFilter Pointer to pointer to returned upstream filter. Must * not be NULL. Set on and only on success. * Returns: * NULL Success. * else Failure error object. */ ErrorObj* upFilter_new( UpFilter** const upFilter) { ErrorObj* errObj; size_t nbytes = sizeof(UpFilter); UpFilter* const filt = (UpFilter*)malloc(nbytes); if (NULL == filt) { errObj = ERR_NEW2(0, NULL, "Couldn't allocate %lu-bytes: %s", (unsigned long)nbytes, strerror(errno)); } else { StringBuf* const strBuf = strBuf_new(132); if (NULL == strBuf) { errObj = ERR_NEW(0, NULL, strBuf_strerror(strBuf)); free(filt); } else { filt->strBuf = strBuf; filt->head = NULL; filt->stringOutOfDate = 1; filt->count = 0; *upFilter = filt; errObj = NULL; /* success */ } } /* "filt" allocated */ return errObj; }
/** * Creates a TCP connection for an LDM client. * * @param addr [in/out] Internet socket address of the LDM server. The * port number is ignored. * @param version [in] Version of the LDM server to use. * @param port [in] The port number of the LDM server. * @param client [out] The client-side transport. Set upon success. The * client should free when it is no longer needed. * @param sock [in] The socket to use for the connection. * @retval NULL Success. "*client" is set. * @return The error object. "*client" is not set. */ static ErrorObj* ldm_clnt_tcp_create( struct sockaddr_in* const addr, /* modified <=> success */ unsigned version, unsigned port, /* 0 => consult portmapper */ CLIENT** const client, /* modified <=> success */ int* const sock) /* modified <=> success */ { struct sockaddr_in ad; CLIENT* clnt; int sck; ErrorObj* error; log_assert(NULL != addr); log_assert(NULL != client); log_assert(NULL != sock); ad = *addr; sck = RPC_ANYSOCK; ad.sin_port = (short)htons((short)port); clnt = clnttcp_create(&ad, LDMPROG, version, &sck, 0, 0); if (clnt) { *client = clnt; *addr = ad; *sock = sck; error = NULL; } else { int code; if (rpc_createerr.cf_stat == RPC_TIMEDOUT) { code = LDM_CLNT_TIMED_OUT; } else if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) { code = LDM_CLNT_UNKNOWN_HOST; } else if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) { code = LDM_CLNT_BAD_VERSION; } else { code = LDM_CLNT_NO_CONNECT; } error = ERR_NEW(code, NULL, clnt_spcreateerror("")); } return error; }
/* * Potentially lengthy operation. */ ErrorObj* ldm_clnt_addr(const char* const name, struct sockaddr_in* addr) { struct sockaddr_in ad; ErrorObj* error; log_assert(NULL != name); log_assert(NULL != addr); if (addrbyhost(name, &ad)) { const char* msg; if (HOST_NOT_FOUND == h_errno) { msg = "no such host is known"; } else if (NO_DATA == h_errno) { msg = "no address for name"; } else if (NO_RECOVERY == h_errno) { msg = "unexpected server failure"; } else if (TRY_AGAIN == h_errno) { msg = "try again later"; } else { msg = "unknown error"; } error = ERR_NEW(h_errno, NULL, msg); } else { *addr = ad; error = NULL; } return error; }
/** * Feeds or notifies a downstream LDM. This function returns either NULL or a * reply to be sent to the downstream LDM (e.g., a RECLASS message) or * terminates this process (hopefully after sending some data). * * @param xprt [in/out] Pointer to server-side transport handle. * @param want [in] Pointer to subscription by downstream LDM. * May contain a "signature" product-specification. * @param isNotifier [in] Whether or not the upstream LDM is a feeder or a * notifier. * @param maxHereis Maximum HEREIS size parameter. Ignored if "isNotifier" * is true. * @return The reply for the downstream LDM or NULL if no reply * should be made. */ static fornme_reply_t* feed_or_notify( SVCXPRT* const xprt, const prod_class_t* const want, const int isNotifier, const max_hereis_t maxHereis) { struct sockaddr_in downAddr = *svc_getcaller(xprt); ErrorObj* errObj; int status; char* downName = NULL; prod_class_t* origSub = NULL; prod_class_t* allowSub = NULL; const signaturet* signature = NULL; UpFilter* upFilter = NULL; fornme_reply_t* reply = NULL; int isPrimary; static fornme_reply_t theReply; static prod_class_t* uldbSub = NULL; /* * Clean-up from a (possibly) previous invocation */ (void)memset(&theReply, 0, sizeof(theReply)); if (uldbSub != NULL) { free_prod_class(uldbSub); uldbSub = NULL; } downName = strdup(hostbyaddr(&downAddr)); if (NULL == downName) { LOG_ADD1("Couldn't duplicate downstream host name: \"%s\"", hostbyaddr(&downAddr)); log_log(LOG_ERR); svcerr_systemerr(xprt); goto return_or_exit; } set_abbr_ident(downName, isNotifier ? "(noti)" : "(feed)"); /* * Remove any "signature" specification from the subscription. */ if ((errObj = separateProductClass(want, &origSub, &signature)) != NULL) { err_log_and_free(errObj, ERR_FAILURE); svcerr_systemerr(xprt); goto free_down_name; } /* * Get the upstream filter */ errObj = lcf_getUpstreamFilter(downName, &downAddr.sin_addr, origSub, &upFilter); if (errObj) { err_log_and_free(ERR_NEW(0, errObj, "Couldn't get \"upstream\" filter"), ERR_FAILURE); svcerr_systemerr(xprt); goto free_orig_sub; } if (NULL == upFilter) { err_log_and_free(ERR_NEW1(0, NULL, "Upstream filter prevents data-transfer: %s", s_prod_class(NULL, 0, origSub)), ERR_FAILURE); svcerr_weakauth(xprt); goto free_orig_sub; } /* TODO: adjust time? */ /* * Reduce the subscription according to what the downstream host is allowed * to receive. */ status = lcf_reduceToAllowed(downName, &downAddr.sin_addr, origSub, &allowSub); if (status == ENOMEM) { LOG_SERROR0("Couldn't compute wanted/allowed product intersection"); log_log(LOG_ERR); svcerr_systemerr(xprt); goto free_up_filter; } if (status == EINVAL) { LOG_ADD1("Invalid pattern in product-class: %s", s_prod_class(NULL, 0, origSub)); log_log(LOG_WARNING); theReply.code = BADPATTERN; reply = &theReply; goto free_up_filter; } assert(status == 0); (void) logIfReduced(origSub, allowSub, "ALLOW entries"); /* * Reduce the subscription according to existing subscriptions from the * same downstream host and, if `isAntiDosEnabled()` returns `true`, * terminate every previously-existing upstream LDM process that's feeding * (not notifying) a subset of the subscription to the same IP address. * * The following relies on atexit()-registered cleanup for removal of the * entry from the upstream LDM database. */ isPrimary = maxHereis > UINT_MAX / 2; status = uldb_addProcess(getpid(), 6, &downAddr, allowSub, &uldbSub, isNotifier, isPrimary); if (status) { LOG_ADD0("Couldn't add this process to the upstream LDM database"); log_log(LOG_ERR); svcerr_systemerr(xprt); goto free_allow_sub; } (void) logIfReduced(allowSub, uldbSub, "existing subscriptions"); /* * Send a RECLASS reply to the downstream LDM if appropriate. */ if (!clss_eq(origSub, uldbSub)) { theReply.code = RECLASS; if (0 < uldbSub->psa.psa_len) { /* * The downstream LDM is allowed less than it requested and was * entered into the upstream LDM database. */ (void)uldb_remove(getpid()); /* maybe next time */ theReply.fornme_reply_t_u.prod_class = uldbSub; } else { /* * The downstream LDM isn't allowed anything and wasn't entered * into the upstream LDM database. */ static prod_class noSub = { { 0, 0 }, /* TS_ZERO */ { 0, 0 }, /* TS_ZERO */ { 0, (prod_spec *) NULL } }; theReply.fornme_reply_t_u.prod_class = &noSub; } reply = &theReply; goto free_allow_sub; } /* * Reply to the downstream LDM that the subscription will be honored. */ theReply.code = OK; theReply.fornme_reply_t_u.id = (unsigned) getpid(); if (!svc_sendreply(xprt, (xdrproc_t)xdr_fornme_reply_t, (caddr_t)&theReply)) { LOG_ADD0("svc_sendreply(...) failure"); log_log(LOG_ERR); svcerr_systemerr(xprt); goto free_allow_sub; } /* * Wait a second before sending anything to the downstream LDM. */ (void) sleep(1); status = isNotifier ? up6_new_notifier(xprt->xp_sock, downName, &downAddr, uldbSub, signature, getQueuePath(), interval, upFilter) : up6_new_feeder(xprt->xp_sock, downName, &downAddr, uldbSub, signature, getQueuePath(), interval, upFilter, isPrimary); svc_destroy(xprt); /* closes the socket */ exit(status); /* * Reply and error handling: */ free_allow_sub: free_prod_class(allowSub); free_up_filter: upFilter_free(upFilter); free_orig_sub: free_prod_class(origSub); free_down_name: free(downName); return_or_exit: return reply; }
/* * Adds a filter-component to an upstream-filter. * * Arguments: * upFilter Pointer to the upstream-filter to which to add the * component. * feedtype The feedtype of the filter-component. * okPattern Pointer to the "OK pattern" of the filter-component to * be added. Caller may free upon return. * notPattern Pointer to the "not pattern" of the filter-component to * be added. May be NULL to indicate that such matching * should be disabled. Caller may free upon return. * Returns: * NULL Success. * else Error object. */ ErrorObj* upFilter_addComponent( UpFilter* const upFilter, const feedtypet feedtype, const Pattern* const okPattern, const Pattern* const notPattern) { ErrorObj* errObj = NULL; /* success */ Element* elt; /* * Ensure that the given feedtype is disjoint from all feedtypes already * in the filter. */ for (elt = upFilter->head; NULL != elt; elt = elt->next) { if (feedtype & elt->ft) { char ftSpec[512]; (void)sprint_feedtypet(ftSpec, sizeof(ftSpec), feedtype); errObj = ERR_NEW2(0, NULL, "Feedtype %s overlaps with feedtype %s", ftSpec, s_feedtypet(elt->ft)); break; } } if (NULL == errObj) { size_t nbytes = sizeof(Element); elt = (Element*)malloc(nbytes); if (NULL == elt) { errObj = ERR_NEW2(0, NULL, "Couldn't allocate %lu-bytes: %s", (unsigned long)nbytes, strerror(errno)); } else { if ((errObj = pat_clone(&elt->okPattern, okPattern))) { errObj = ERR_NEW(0, errObj, "Couldn't clone \"OK\" pattern"); free(elt); } else { if (NULL == notPattern) { elt->notPattern = NULL; } else { if ((errObj = pat_clone(&elt->notPattern, notPattern))) { errObj = ERR_NEW(0, errObj, "Couldn't clone \"not\" pattern"); pat_free(elt->okPattern); free(elt); elt = NULL; } } if (elt) { elt->ft = feedtype; elt->next = upFilter->head; upFilter->head = elt; upFilter->stringOutOfDate = 1; upFilter->count++; } } /* "elt->okPattern" allocated */ } /* "elt" allocated */ } // given feedtype is disjoint from those already in filter return errObj; }