/* * Opens a shared-counter. * * Arguments: * path Pathname of the stat()able file to be associated with * the shared-counter. * *sc Pointer to shared-counter. Set on and only on success. * * Returns: * NULL Success * !NULL Failure: * SC_SYSTEM */ ErrorObj* sc_open( const char* path, SharedCounter** const sc) { ErrorObj* error; size_t nbytes = sizeof(SharedCounter); SharedCounter* ptr = malloc(nbytes); if (NULL == ptr) { error = ERR_NEW2(SC_SYSTEM, NULL, "Couldn't allocate %lu bytes: %s", (unsigned long)nbytes, strerror(errno)); } else { key_t key = ftok(path, 0); if ((key_t)-1 == key) { error = ERR_NEW1(SC_SYSTEM, NULL, "Couldn't create key for shared-memory segment: %s", strerror(errno)); } else { int shmid = shmget(key, sizeof(unsigned), 0600 | IPC_CREAT); if (-1 == shmid) { error = ERR_NEW1(SC_SYSTEM, NULL, "Couldn't get shared-memory segment: %s", strerror(errno)); } else { void* const counter = shmat(shmid, NULL, 0); if ((void*)-1 == counter) { error = ERR_NEW1(SC_SYSTEM, NULL, "Couldn't attach shared-memory segment: %s", strerror(errno)); } else { ptr->shmid = shmid; ptr->counter = (unsigned*)counter; *sc = ptr; error = NULL; /* success */ } /* shared-memory segment attached */ if (error) (void)shmctl(shmid, IPC_RMID, NULL); } /* got shared-memory segment */ } /* got key for shared memory segment */ if (error) free(ptr); } /* ptr allocated */ return error; }
/* * Separates a product-class into a signature component and a non-signature * component. * * Arguments: * prodClass Pointer to product-class to be separated. Caller may * free upon return. * noSigProdClass Pointer to pointer to be set to an allocated product- * class that will not have a signature encoded within * it. Set on and only on success. On success, caller * should invoke free_prod_class(*noSigProdClass). * signature Pointer to pointer to signature. Set on and only on * success. Will be set to NULL if and only if "prodClass" * didn't contain an encoded signature; otherwise, will * be set to point to a static buffer that contains the * signature. Caller must not free. * Returns: * NULL Success. * else Error object. */ static ErrorObj* separateProductClass( const prod_class_t* const prodClass, prod_class_t** const noSigProdClass, const signaturet** const signature) { ErrorObj* errObj; prod_class_t* noSigClass = dup_prod_class(prodClass); if (NULL == noSigClass) { errObj = ERR_NEW1(0, NULL, "Couldn't duplicate product-class: %s", strerror(errno)); } else { const signaturet* sig = decodeSignature(prodClass); if (NULL != sig) clss_scrunch(noSigClass); /* removes encoded signature */ *noSigProdClass = noSigClass; *signature = sig; errObj = NULL; /* success */ } return errObj; }
/* * Closes a shared-counter. * * Arguments: * sc Pointer to shared-counter. May be NULL. * * Returns: * NULL Success. * !NULL Failure: * SC_SYSTEM */ ErrorObj* sc_close( SharedCounter* const sc) { ErrorObj* error = NULL; /* success */ if (NULL != sc) { if (NULL != sc->counter) { shmid_ds stat; if (-1 == shmdt(sc->counter)) { error = ERR_NEW1(SC_ERROR, NULL, "Couldn't detatch shared-memory segment: %s", strerror(errno)); } else { if (-1 == shmctl(sc->shmid, IPC_STAT, &stat)) { error = ERR_NEW1(SC_ERROR, NULL, "Couldn't get status of shared-memory segment: %s", strerror(errno)); } else { if (0 == stat->shm_nattch) { if (-1 == shmctl(sc->shmid, IPC_RMID, NULL)) { error = ERR_NEW1(SC_ERROR, NULL, "Couldn't destroy shared-memory segment: %s", strerror(errno)); } } sc->counter = NULL; sc->shmid = -1; } } } free(sc); } return error; }
/* * Decodes a data-product signature from the last product-specification of a * product-class if it exists. * * Arguments: * class Pointer to the product-class. Caller may free upon * return. * Returns: * NULL The last product-specification didn't contain a valid, * encoded signature. * else Pointer to a static signature buffer into which the * signature specification was successfully decoded. */ static const signaturet* decodeSignature( const prod_class_t* const prodClass) { const signaturet* sig = NULL; /* no valid, encoded signature */ if (0 < prodClass->psa.psa_len) { const prod_spec* const lastProdSpec = &prodClass->psa.psa_val[prodClass->psa.psa_len - 1]; if (NONE == lastProdSpec->feedtype) { char* pat = lastProdSpec->pattern; if (strncasecmp("SIG=", pat, 4) == 0) { char* encodedSig = pat + 4; int i; unsigned value; static signaturet sigBuf; errno = 0; for (i = 0; i < sizeof(signaturet); i++) { if (sscanf(encodedSig + 2 * i, "%2x", &value) != 1) break; sigBuf[i] = (unsigned char) value; } if (i == sizeof(signaturet)) { sig = (const signaturet*) &sigBuf[0]; } else { if (0 == errno) { err_log_and_free( ERR_NEW1(1, NULL, "Invalid signature (%s)", encodedSig), ERR_NOTICE); } else { err_log_and_free( ERR_NEW2(1, NULL, "Invalid signature (%s): %s", encodedSig, strerror(errno)), ERR_NOTICE); } } /* signature not decoded */ } /* "SIG=" found */ } /* last feedtype is NONE */ } /* at least one product-specification */ return sig; }
/* * Attempts to connect to an upstream LDM using a range of LDM versions. The * versions are tried, in order, from highest to lowest. This function returns * on the first successful attempt. If the host is unknown or the RPC call * times-out, then the version-loop is prematurely terminated and this function * returns immediately. * * The client is responsible for freeing the client resources set by this * function on success. Calls exitIfDone() after potentially lengthy * operations. * * Arguments: * upName The name of the upstream LDM host. * port The port on which to connect. * version Program version. * *client Pointer to CLIENT structure. Set on success. * *socket The socket used for the connection. May be NULL. * *upAddr The IP address of the upstream LDM host. Set on * success. May be NULL. * Returns: * NULL Success. *vers_out, *client, *sock_out, and *upAddr * set. * !NULL Error. "*client" is not set. err_code(RETURN_VALUE): * LDM_CLNT_UNKNOWN_HOST Unknown upstream host. * LDM_CLNT_TIMED_OUT Call to upstream host timed-out. * LDM_CLNT_BAD_VERSION Upstream LDM isn't given version. * LDM_CLNT_NO_CONNECT Other connection-related error. * LDM_CLNT_SYSTEM_ERROR A fatal system-error occurred. */ ErrorObj* ldm_clnttcp_create_vers( const char* const upName, const unsigned port, unsigned const version, CLIENT** const client, int* const socket, struct sockaddr_in* upAddr) { ErrorObj* error; struct sockaddr_in addr; log_assert(upName != NULL); log_assert(client != NULL); /* * Get the IP address of the upstream LDM. This is a potentially * lengthy operation. */ (void)exitIfDone(0); error = ldm_clnt_addr(upName, &addr); if (error) { error = ERR_NEW1(LDM_CLNT_UNKNOWN_HOST, error, "Couldn't get IP address of host %s", upName); } else { int sock; int errCode; CLIENT* clnt = NULL; /* * Connect to the remote port. This is a potentially lengthy * operation. */ (void)exitIfDone(0); error = ldm_clnt_tcp_create(&addr, version, port, &clnt, &sock); if (error) { errCode = err_code(error); if (LDM_CLNT_NO_CONNECT != errCode) { error = ERR_NEW3(errCode, error, "Couldn't connect to LDM %d on %s " "using port %d", version, upName, port); } else { err_log_and_free( ERR_NEW3(0, error, "Couldn't connect to LDM %d on %s using port " "%d", version, upName, port), ERR_INFO); /* * Connect using the portmapper. This is a * potentially lengthy operation. */ (void)exitIfDone(0); error = ldm_clnt_tcp_create(&addr, version, 0, &clnt, &sock); if (error) { error = ERR_NEW2(err_code(error), error, "Couldn't connect to LDM on %s " "using either port %d or portmapper", upName, port); } /* portmapper failure */ } /* non-fatal port failure */ } /* port failure */ if (!error) { /* * Success. Set the return arguments. */ *client = clnt; if (socket) *socket = sock; if (upAddr) *upAddr = addr; } } /* got upstream IP address */ 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; }