/* 
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved 
 * Use is subject to license terms. 
 * Some preexisting portions Copyright 1998-2001 Netscape Communications Corp.
 * All rights reserved.
 */
/************************************************************

 testgetip.c

 This source file provides an example of a pre-operation plug-in
 function that gets the IP address of the client and the IP 
 address of the server.

 * testgetip (Called before an LDAP search operation)
   Logs the server and client IP addresses.

 ACTIVATING THIS PLUG-IN
 -----------------------
 1. Build the library containing the plug-in.

 2. Configure the server to log plug-in messages.

    $ dsconf set-log-prop error level:err-plugins

 3. Configure the server to load the plug-in.
     
    $ dsconf create-plugin -H <lib-path> -F testgetip_init -Y preoperation "Test GetIP"
    $ dsconf set-plugin-prop "Test GetIP" depends-on-type:database feature:test-getip version:7.0 desc:"Sample plug-in to log server and client IP addresses"
    $ dsconf enable-plugin "Test GetIP"
    Directory Server needs to be restarted for changes to take effect
    $ dsadm restart <inst-path>

 ************************************************************/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "slapi-plugin.h"
#include "nspr.h"

Slapi_PluginDesc getippdesc = {
    "test-getip",                      /* plug-in identifier      */
    "Oracle Corporation (test)",       /* vendor name             */
    "11.1.1.3.0",                      /* plug-in revision number */
    "Sample plug-in to log server and client IP addresses" /* desc. */
};

/* Translate a PRNetAddr to a string.                               */
char * ip2str(PRNetAddr ip_addr)
{
    char * ip_str;
    char   buf[256] = {0};

    if (
        ip_addr.ipv6.ip.pr_s6_addr32[0] != 0 ||
        ip_addr.ipv6.ip.pr_s6_addr32[1] != 0 ||
        ip_addr.ipv6.ip.pr_s6_addr32[2] != 0 ||
        ip_addr.ipv6.ip.pr_s6_addr32[3] != 0
    ) {
        if (PR_IsNetAddrType(&ip_addr, PR_IpAddrV4Mapped)) {
            PRNetAddr v4addr;
            memset(&v4addr, '\0', sizeof(v4addr));
            v4addr.inet.family = PR_AF_INET;
            v4addr.inet.ip     = ip_addr.ipv6.ip.pr_s6_addr32[3];
            PR_NetAddrToString(&v4addr, buf, sizeof(buf));
        } else {
            PR_NetAddrToString(&ip_addr, buf, sizeof(buf));
        }
        buf[sizeof(buf)-1] = '\0';     /* Successful translation.   */
        ip_str = buf;
    } else {
        ip_str = "0.0.0.0";            /* Address unknown.          */
    }

    return (slapi_ch_strdup(ip_str));
}

/* Log server and client IP addresses.                              */
int
testgetip(Slapi_PBlock * pb)
{
    PRNetAddr   client_addr, server_addr; /* Portable IP addresses  */
    char      * client_ip, * server_ip;   /* Addresses as strings   */
    int         connId, opId, rc = 0;
    long        msgId;
    
    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId);
    if (rc != 0) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            SLAPI_LOG_NO_MSGID,
            SLAPI_LOG_NO_CONNID,
            SLAPI_LOG_NO_OPID,
            "testgetip in test-getip plug-in",
            "Could not obtain connection information.\n"
        );
        return (rc);
    }
                
    /* Log the client IP address.                                   */
    rc  |= slapi_pblock_get(pb, SLAPI_CONN_CLIENTNETADDR, &client_addr);
    if (rc != 0) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "testgetip in test-getip plug-in",
            "Could not get client IP.\n"
        );
        return (0);
    }
    client_ip = ip2str(client_addr);
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        msgId,
        connId,
        opId,
        "testgetip in test-getip plug-in",
        "Client's IP address is %s\n", client_ip
    );
    slapi_ch_free_string(&client_ip);
                
    /* Log the destination (server) IP address.                     */
    rc  |= slapi_pblock_get(pb, SLAPI_CONN_SERVERNETADDR, &server_addr);
    if (rc != 0) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "testgetip in test-getip plug-in",
            "Could not get server IP.\n"
        );
        return (0);
    }
    server_ip = ip2str(server_addr);
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        msgId,
        connId,
        opId,
        "testgetip in test-getip plug-in",
        "Client sent request to server IP %s\n", server_ip
    );
    slapi_ch_free_string(&server_ip);
                
    return (rc);
}

/* Register the plug-in with the server.                            */
#ifdef _WIN32
__declspec(dllexport)
#endif
int
testgetip_init(Slapi_PBlock * pb)
{
    int rc = 0;                        /* 0 means success           */
    rc |= slapi_pblock_set(            /* Plug-in API version       */
        pb,
        SLAPI_PLUGIN_VERSION,
        SLAPI_PLUGIN_CURRENT_VERSION
    );
    rc |= slapi_pblock_set(            /* Plug-in description       */
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *) &getippdesc
    );
    rc |= slapi_pblock_set(            /* Log IP@ on search request */
        pb,
        SLAPI_PLUGIN_PRE_SEARCH_FN,
        (void *) testgetip
    );
    return (rc);
}
