
/* General includes */
#include <sys/types.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <unistd.h>
#else
#include <winsock.h>
#include <direct.h>
#endif
/* Apache includes */
#include <httpd.h>
#include <http_config.h>
#include <http_log.h>
#include <scoreboard.h>
#include <apr_optional.h>
#include <apr_strings.h>
#include <apr_shm.h>

/* UCD-SNMP includes */
#include <ucd-snmp-config.h>
#include <asn1.h>
#include <snmp_api.h>
#include <snmp_vars.h>
#include <default_store.h>

/* module includes */
#include "covalent-snmp-config.h"
#include "snmpcommon/snmpcommon.h"
#include "snmpcommon/generic-logging.h"
/*
 * Wrapper around the sendto to ease porting.
 */
SNMPCOMMON_DECLARE(int)
snmpcommon_logpath_write(snmpcommon_logpath_t *logpath,
						 void *data, int datalen) 
{
	if (logpath) {
        datalen = sendto(logpath->socket[ORIGINATOR], (char *)data, datalen, 0,
                (struct sockaddr *)&(logpath->address[RECIPIENT]),
				(logpath->address_size[RECIPIENT]));
	}
	return(datalen);
}

/*
 * Wrapper around the read to ease porting.
 */
SNMPCOMMON_DECLARE(int)
snmpcommon_logpath_read(snmpcommon_logpath_t *logpath,
						void *data, int datalen)
{
struct sockaddr fromaddr;
int fromaddrlength = sizeof(struct sockaddr);

	if (logpath) {
            datalen = recvfrom(logpath->socket[RECIPIENT], data, datalen,
                               0, &fromaddr, &fromaddrlength);
            if (datalen < 0) {
                /* something bad happened */
		    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL,
				"SNMP: recvfrom failed: '%s'\n", strerror(errno));
            }
	}
	return(datalen);
}

/*
 * Wrapper to abstract from the address record.
 */
SNMPCOMMON_DECLARE(int)
snmpcommon_logpath_socket(snmpcommon_logpath_t *logpath, const int who)
{
    if (logpath) {
        return(logpath->socket[who]);
	}
	return -1;
}

/*
 * Open the logpath
 */
SNMPCOMMON_DECLARE(snmpcommon_logpath_t *)
snmpcommon_logpath_open(const int who,
						char *key, apr_pool_t *p)
{
#ifndef WIN32
snmpcommon_logpath_t *logpath;

    apr_pool_userdata_get((void **)&logpath, key, p);
    if (logpath == NULL) {
        logpath = apr_palloc(p, sizeof(snmpcommon_logpath_t));
        logpath->socket[who] = socket(AF_INET, SOCK_DGRAM, 0);
        if (logpath->socket[who] < 0) {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot open 'logpath' socket\n");
            return(NULL);
        }
        memset(&(logpath->address[who]), 0, sizeof(logpath->address[who]));
        logpath->address[who].sin_family = AF_INET;
        logpath->address[who].sin_addr.s_addr = inet_addr("127.0.0.1");
        logpath->address[who].sin_port = htons(0);
        logpath->address_size[who] = sizeof(logpath->address[who]);
        if (bind(logpath->socket[who], (struct sockaddr *)&logpath->address[who],
                                    logpath->address_size[who]) < 0){
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot bind 'logpath' socket\n");
            return(NULL);
        }
        if (getsockname(logpath->socket[who], (struct sockaddr *)&logpath->address[who],
                                  &(logpath->address_size[who]))) {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot get 'logpath' options\n");
            return(NULL);
        }
        apr_pool_userdata_set((const void*)logpath, key,
                                apr_pool_cleanup_null, p);
    }
    return(logpath);
#else /* WINW32 */
snmpcommon_logpath_t *logpath;
apr_shm_t *shmem;
apr_size_t shmem_size = sizeof(snmpcommon_logpath_t);
char *shmem_name;
apr_status_t result;

    if (ds_get_string(DS_LIBRARY_ID, DS_LIB_PERSISTENT_DIR) == NULL) {
	    ds_set_string(DS_LIBRARY_ID, DS_LIB_PERSISTENT_DIR,
			                         COVALENT_SNMP_PERSISTENT_DIR);
	}
    shmem_name = apr_pstrcat(p, ap_server_root_relative(p, 
	                               ds_get_string(DS_LIBRARY_ID,
				                 DS_LIB_PERSISTENT_DIR)),
							"/", key, NULL);

	if (who == RECIPIENT) {
		result = apr_shm_create(&shmem, shmem_size, shmem_name, p);
                if (result != APR_SUCCESS) {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                 "SNMP: cannot create shared memory segment\n");
                    return(NULL);
                }
		logpath = apr_shm_baseaddr_get(shmem);
		shmem_size = apr_shm_size_get(shmem);
        if ((logpath != NULL) &&
			(shmem_size >= sizeof(snmpcommon_logpath_t))) {
            memset(logpath, 0, sizeof(snmpcommon_logpath_t));
            logpath->socket[who] = socket(AF_INET, SOCK_DGRAM, 0);
            if (logpath->socket[who] < 0) {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot open 'logpath' socket\n");
                return(NULL);
            }
            logpath->address[who].sin_family = AF_INET;
            logpath->address[who].sin_addr.s_addr = inet_addr("127.0.0.1");
            logpath->address[who].sin_port = htons(0);
            logpath->address_size[who] = sizeof(logpath->address[who]);
            if (bind(logpath->socket[who], (struct sockaddr *)&logpath->address[who],
                                    logpath->address_size[who]) < 0){
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot bind 'logpath' socket\n");
                return(NULL);
	    }
            if (getsockname(logpath->socket[who], (struct sockaddr *)&logpath->address[who],
                                  &(logpath->address_size[who]))) {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot get 'logpath' options\n");
                return(NULL);
			}
		}
	} else if (who == ORIGINATOR) {
		result = apr_shm_attach(&shmem, shmem_name, p);
                if (result != APR_SUCCESS) {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                 "SNMP: cannot attach to shared memory segment\n");
                    return(NULL);
                }
		logpath = apr_shm_baseaddr_get(shmem);
		shmem_size = apr_shm_size_get(shmem);
        if ((logpath != NULL) &&
			(shmem_size >= sizeof(snmpcommon_logpath_t))) {
            logpath->socket[who] = socket(AF_INET, SOCK_DGRAM, 0);
            if (logpath->socket[who] < 0) {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot open 'logpath' socket\n");
                return(NULL);
			}
            logpath->address[who].sin_family = AF_INET;
            logpath->address[who].sin_addr.s_addr = inet_addr("127.0.0.1");
            logpath->address[who].sin_port = htons(0);
            logpath->address_size[who] = sizeof(logpath->address[who]);
            if (bind(logpath->socket[who], (struct sockaddr *)&logpath->address[who],
                                    logpath->address_size[who]) < 0){
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot bind 'logpath' socket\n");
                return(NULL);
			}
            if (getsockname(logpath->socket[who], (struct sockaddr *)&logpath->address[who],
                                  &(logpath->address_size[who]))) {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                                     "SNMP: cannot get 'logpath' options\n");
                return(NULL);
			}
		}
    }
    return(logpath);
#endif /* ! WIN32 */
}

/*
 * Close the logpath.
 */
SNMPCOMMON_DECLARE(void)
snmpcommon_logpath_close(snmpcommon_logpath_t *logpath, const int who)
{
	if (logpath) {
#ifndef WIN32
        close(logpath->socket[who]);
#else
        closesocket(logpath->socket[who]);
#endif
	}
}


