Example #1
0
/*
 * 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;
}
Example #2
0
/*
 * 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;
}
Example #3
0
/*
 * 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;
}
Example #4
0
/*
 * 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;
}
Example #5
0
/*
 * 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;
}
Example #6
0
/**
 * 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;
}