/*
 * snmpvacm.c - send snmp SET requests to a network entity to change the
 *             vacm database
 *
 */
#include <net-snmp/net-snmp-config.h>

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <stdio.h>
#include <ctype.h>
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

#include <net-snmp/net-snmp-includes.h>

#define CMD_CREATESEC2GROUP_NAME    "createSec2Group"
#define CMD_CREATESEC2GROUP         1
#define CMD_DELETESEC2GROUP_NAME    "deleteSec2Group"
#define CMD_DELETESEC2GROUP         2
#define CMD_CREATEACCESS_NAME    	"createAccess"
#define CMD_CREATEACCESS         	3
#define CMD_DELETEACCESS_NAME 		"deleteAccess"
#define CMD_DELETEACCESS      		4
#define CMD_CREATEVIEW_NAME 		"createView"
#define CMD_CREATEVIEW      		5
#define CMD_DELETEVIEW_NAME 		"deleteView"
#define CMD_DELETEVIEW      		6
#define CMD_CREATEAUTH_NAME     	"createAuth"
#define CMD_CREATEAUTH          	7
#define CMD_DELETEAUTH_NAME 		"deleteAuth"
#define CMD_DELETEAUTH      		8

#define CMD_NUM    8

static const char *successNotes[CMD_NUM] = {
    "Sec2group successfully created.",
    "Sec2group successfully deleted.",
    "Access successfully created.",
    "Access successfully deleted.",
    "View successfully created.",
    "View successfully deleted.",
    "AuthAccess successfully created.",
    "AuthAccess successfully deleted."
};

#define                   SEC2GROUP_OID_LEN	11
#define                   ACCESS_OID_LEN   	11
#define                   VIEW_OID_LEN    	12
#define                   AUTH_OID_LEN   	12

static oid      vacmGroupName[MAX_OID_LEN] =
    { 1, 3, 6, 1, 6, 3, 16, 1, 2, 1, 3 },
    vacmSec2GroupStatus[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 2, 1, 5}, vacmAccessContextMatch[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 4, 1, 4}, vacmAccessReadViewName[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 4, 1, 5}, vacmAccessWriteViewName[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 4, 1, 6}, vacmAccessNotifyViewName[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 4, 1, 7}, vacmAccessStatus[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 4, 1, 9}, vacmViewTreeFamilyMask[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 5, 2, 1, 3}, vacmViewTreeFamilyType[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 5, 2, 1, 4},
    vacmViewTreeFamilyStatus[MAX_OID_LEN] = {
1, 3, 6, 1, 6, 3, 16, 1, 5, 2, 1, 6}

;

#define NSVACMACCESSTABLE    1, 3, 6, 1, 4, 1, 8072, 1, 9, 1
static oid nsVacmContextPfx[MAX_OID_LEN]  = { NSVACMACCESSTABLE, 1, 2 };
static oid nsVacmViewName[MAX_OID_LEN]    = { NSVACMACCESSTABLE, 1, 3 };
static oid nsVacmRowStatus[MAX_OID_LEN]   = { NSVACMACCESSTABLE, 1, 5 };

int             viewTreeFamilyType = 1;

void
usage(void)
{
    fprintf(stderr, "Usage: snmpvacm ");
    snmp_parse_args_usage(stderr);
    fprintf(stderr, " COMMAND\n\n");
    snmp_parse_args_descriptions(stderr);
    fprintf(stderr, "\nsnmpvacm commands:\n");
    fprintf(stderr, "        createAccess     GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL CONTEXTMATCH READVIEWNAME WRITEVIEWNAME NOTIFYVIEWNAME\n");
    fprintf(stderr, "        deleteAccess     GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL\n");
    fprintf(stderr, "        createSec2Group  MODEL SECURITYNAME  GROUPNAME\n");
    fprintf(stderr, "        deleteSec2Group  MODEL SECURITYNAME\n");
    fprintf(stderr, "  [-Ce] createView       NAME SUBTREE [MASK]\n");
    fprintf(stderr, "        deleteView       NAME SUBTREE\n");
    fprintf(stderr, "        createAuth       GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL AUTHTYPE CONTEXTMATCH VIEWNAME\n");
    fprintf(stderr, "        deleteAuth       GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL AUTHTYPE\n");
}


void
auth_oid(oid * it, size_t * len, const char *groupName,
           const char *prefix, int model, int level, const char *authtype)
{
    int             i;
    int             itIndex = AUTH_OID_LEN;

    it[itIndex++] = strlen(groupName);
    for (i = 0; i < (int) strlen(groupName); i++)
        it[itIndex++] = groupName[i];

    if (prefix) {
        *len += strlen(prefix);
        it[itIndex++] = strlen(prefix);
        for (i = 0; i < (int) strlen(prefix); i++)
            it[itIndex++] = prefix[i];
    } else
        it[itIndex++] = 0;

    it[itIndex++] = model;
    it[itIndex++] = level;

    it[itIndex++] = strlen(authtype);
    for (i = 0; i < (int) strlen(authtype); i++)
        it[itIndex++] = authtype[i];

    *len = itIndex;
}

void
access_oid(oid * it, size_t * len, const char *groupName,
           const char *prefix, int model, int level)
{
    int             i;

    int             itIndex = ACCESS_OID_LEN;

    *len = itIndex + 4 + +strlen(groupName);

    it[itIndex++] = strlen(groupName);
    for (i = 0; i < (int) strlen(groupName); i++)
        it[itIndex++] = groupName[i];

    if (prefix) {
        *len += strlen(prefix);
        it[itIndex++] = strlen(prefix);
        for (i = 0; i < (int) strlen(prefix); i++)
            it[itIndex++] = prefix[i];
    } else
        it[itIndex++] = 0;

    it[itIndex++] = model;
    it[itIndex++] = level;
}


void
sec2group_oid(oid * it, size_t * len, int model, const char *name)
{
    int             i;

    int             itIndex = SEC2GROUP_OID_LEN;

    *len = itIndex + 2 + strlen(name);

    it[itIndex++] = model;

    it[itIndex++] = strlen(name);
    for (i = 0; i < (int) strlen(name); i++)
        it[itIndex++] = name[i];
}

void
view_oid(oid * it, size_t * len, const char *viewName, char *viewSubtree)
{
    int             i;
    oid             c_oid[SPRINT_MAX_LEN];
    size_t          c_oid_length = SPRINT_MAX_LEN;

    int             itIndex = VIEW_OID_LEN;

    if (!snmp_parse_oid(viewSubtree, c_oid, &c_oid_length)) {
        printf("Error parsing subtree (%s)\n", viewSubtree);
        exit(1);
    }

    *len = itIndex + 2 + strlen(viewName) + c_oid_length;

    it[itIndex++] = strlen(viewName);
    for (i = 0; i < (int) strlen(viewName); i++)
        it[itIndex++] = viewName[i];

    
    it[itIndex++] = c_oid_length;
    for (i = 0; i < (int) c_oid_length; i++)
        it[itIndex++] = c_oid[i];

    /*
     * sprint_objid(c_oid, it, *len); 
     */
}

static void
optProc(int argc, char *const *argv, int opt)
{
    switch (opt) {
    case 'C':
        while (*optarg) {
            switch (*optarg++) {
            case 'e':
                viewTreeFamilyType = 2;
                break;

            default:
                fprintf(stderr,
                        "Unknown flag passed to -C: %c\n", optarg[-1]);
                exit(1);
            }
        }
        break;
    }
}


int
main(int argc, char *argv[])
{
    netsnmp_session session, *ss;
    netsnmp_pdu    *pdu = NULL, *response = NULL;
#ifdef notused
    netsnmp_variable_list *vars;
#endif

    int             arg;
#ifdef notused
    int             count;
    int             current_name = 0;
    int             current_type = 0;
    int             current_value = 0;
    char           *names[128];
    char            types[128];
    char           *values[128];
    oid             name[MAX_OID_LEN];
#endif
    size_t          name_length;
    int             status;
    int             exitval = 1;
    int             command = 0;
    long            longvar;
    int             secModel, secLevel, contextMatch;
    unsigned int    val, i = 0;
    char           *mask, *groupName, *prefix, *authtype;
    u_char          viewMask[VACMSTRINGLEN];
    char           *st;

    SOCK_STARTUP;

    /*
     * get the common command line arguments 
     */
    switch (arg = snmp_parse_args(argc, argv, &session, "C:", optProc)) {
    case NETSNMP_PARSE_ARGS_ERROR:
        goto out;
    case NETSNMP_PARSE_ARGS_SUCCESS_EXIT:
        exitval = 0;
        goto out;
    case NETSNMP_PARSE_ARGS_ERROR_USAGE:
        usage();
        goto out;
    default:
        break;
    }

    /*
     * open an SNMP session 
     */
    /*
     * Note:  this wil obtain the engineID needed below 
     */
    ss = snmp_open(&session);
    if (ss == NULL) {
        /*
         * diagnose snmp_open errors with the input netsnmp_session pointer 
         */
        snmp_sess_perror("snmpvacm", &session);
        goto out;
    }

    /*
     * create PDU for SET request and add object names and values to request 
     */
    pdu = snmp_pdu_create(SNMP_MSG_SET);

    if (arg >= argc) {
        fprintf(stderr, "Please specify a operation to perform.\n");
        usage();
        goto close_session;
    }

    if (strcmp(argv[arg], CMD_DELETEVIEW_NAME) == 0)
        /*
         * deleteView: delete a view
         *
         * deleteView NAME SUBTREE
         *
         */
    {
        if (++arg + 2 != argc) {
            fprintf(stderr, "You must specify the view to delete\n");
            usage();
            goto close_session;
        }

        command = CMD_DELETEVIEW;
        name_length = VIEW_OID_LEN;
        view_oid(vacmViewTreeFamilyStatus, &name_length, argv[arg],
                 argv[arg + 1]);
        longvar = RS_DESTROY;
        snmp_pdu_add_variable(pdu, vacmViewTreeFamilyStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));
    } else if (strcmp(argv[arg], CMD_CREATEVIEW_NAME) == 0)
        /*
         * createView: create a view
         *
         * createView NAME SUBTREE MASK
         *
         */
    {
        if (++arg + 2 > argc) {
            fprintf(stderr, "You must specify name, subtree and mask\n");
            usage();
            goto close_session;
        }
        command = CMD_CREATEVIEW;
        name_length = VIEW_OID_LEN;
        view_oid(vacmViewTreeFamilyStatus, &name_length, argv[arg],
                 argv[arg + 1]);
        longvar = RS_CREATEANDGO;
        snmp_pdu_add_variable(pdu, vacmViewTreeFamilyStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));
        /*
         * Mask
         */
        if (arg + 3 == argc) {
            mask = argv[arg + 2];
            for (mask = strtok_r(mask, ".:", &st); mask; mask = strtok_r(NULL, ".:", &st)) {
                if (i >= sizeof(viewMask)) {
                    printf("MASK too long\n");
                    goto close_session;
                }
                if (sscanf(mask, "%x", &val) == 0) {
                    printf("invalid MASK\n");
                    goto close_session;
                }
                viewMask[i] = val;
                i++;
            }
	} else {
            for (i=0 ; i < (name_length+7)/8; i++)
                viewMask[i] = (u_char)0xff;
        }
        view_oid(vacmViewTreeFamilyMask, &name_length, argv[arg],
                 argv[arg + 1]);
        snmp_pdu_add_variable(pdu, vacmViewTreeFamilyMask, name_length,
                              ASN_OCTET_STR, viewMask, i);

        view_oid(vacmViewTreeFamilyType, &name_length, argv[arg],
                 argv[arg + 1]);
        snmp_pdu_add_variable(pdu, vacmViewTreeFamilyType, name_length,
                              ASN_INTEGER, (u_char *) & viewTreeFamilyType,
                              sizeof(viewTreeFamilyType));

    } else if (strcmp(argv[arg], CMD_DELETESEC2GROUP_NAME) == 0)
        /*
         * deleteSec2Group: delete security2group
         *
         * deleteSec2Group  MODEL SECURITYNAME
         *
         */
    {
        if (++arg + 2 != argc) {
            fprintf(stderr, "You must specify the sec2group to delete\n");
            usage();
            goto close_session;
        }

        command = CMD_DELETESEC2GROUP;
        name_length = SEC2GROUP_OID_LEN;
        if (sscanf(argv[arg], "%d", &secModel) == 0) {
            printf("invalid security model\n");
            usage();
            goto close_session;
        }
        sec2group_oid(vacmSec2GroupStatus, &name_length, secModel,
                      argv[arg + 1]);
        longvar = RS_DESTROY;
        snmp_pdu_add_variable(pdu, vacmSec2GroupStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));
    } else if (strcmp(argv[arg], CMD_CREATESEC2GROUP_NAME) == 0)
        /*
         * createSec2Group: create a security2group
         *
         * createSec2Group  MODEL SECURITYNAME GROUPNAME
         *
         */
    {
        if (++arg + 3 != argc) {
            fprintf(stderr,
                    "You must specify model, security name and group name\n");
            usage();
            goto close_session;
        }

        command = CMD_CREATESEC2GROUP;
        name_length = SEC2GROUP_OID_LEN;
        if (sscanf(argv[arg], "%d", &secModel) == 0) {
            printf("invalid security model\n");
            usage();
            goto close_session;
        }
        sec2group_oid(vacmSec2GroupStatus, &name_length, secModel,
                      argv[arg + 1]);
        longvar = RS_CREATEANDGO;
        snmp_pdu_add_variable(pdu, vacmSec2GroupStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));
        sec2group_oid(vacmGroupName, &name_length, secModel,
                      argv[arg + 1]);
        snmp_pdu_add_variable(pdu, vacmGroupName, name_length,
                              ASN_OCTET_STR, (u_char *) argv[arg + 2],
                              strlen(argv[arg + 2]));
    } else if (strcmp(argv[arg], CMD_DELETEACCESS_NAME) == 0)
        /*
         * deleteAccess: delete access entry
         *
         * deleteAccess  GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL
         *
         */
    {
        if (++arg + 3 > argc) {
            fprintf(stderr,
                    "You must specify the access entry to delete\n");
            usage();
            goto close_session;
        }

        command = CMD_DELETEACCESS;
        name_length = ACCESS_OID_LEN;
        groupName = argv[arg];
        if (arg + 4 == argc)
            prefix = argv[++arg];
        else
            prefix = NULL;

        if (sscanf(argv[arg + 1], "%d", &secModel) == 0) {
            printf("invalid security model\n");
            usage();
            goto close_session;
        }
        if (sscanf(argv[arg + 2], "%d", &secLevel) == 0) {
            printf("invalid security level\n");
            usage();
            goto close_session;
        }
        access_oid(vacmAccessStatus, &name_length, groupName, prefix,
                   secModel, secLevel);
        longvar = RS_DESTROY;
        snmp_pdu_add_variable(pdu, vacmAccessStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));
    } else if (strcmp(argv[arg], CMD_CREATEACCESS_NAME) == 0)
        /*
         * createAccess: create access entry
         *
         * createAccess  GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL CONTEXTMATCH READVIEWNAME WRITEVIEWNAME NOTIFYVIEWNAME
         *
         */
    {
        if (++arg + 7 > argc) {
            fprintf(stderr,
                    "You must specify the access entry to create\n");
            usage();
            goto close_session;
        }

        command = CMD_CREATEACCESS;
        name_length = ACCESS_OID_LEN;
        groupName = argv[arg];
        if (arg + 8 == argc)
            prefix = argv[++arg];
        else
            prefix = NULL;

        if (sscanf(argv[arg + 1], "%d", &secModel) == 0) {
            printf("invalid security model\n");
            usage();
            goto close_session;
        }
        if (sscanf(argv[arg + 2], "%d", &secLevel) == 0) {
            printf("invalid security level\n");
            usage();
            goto close_session;
        }
        access_oid(vacmAccessStatus, &name_length, groupName, prefix,
                   secModel, secLevel);
        longvar = RS_CREATEANDGO;
        snmp_pdu_add_variable(pdu, vacmAccessStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));

        access_oid(vacmAccessContextMatch, &name_length, groupName, prefix,
                   secModel, secLevel);
        if (sscanf(argv[arg + 3], "%d", &contextMatch) == 0) {
            printf("invalid contextMatch\n");
            usage();
            goto close_session;
        }
        snmp_pdu_add_variable(pdu, vacmAccessContextMatch, name_length,
                              ASN_INTEGER, (u_char *) & contextMatch,
                              sizeof(contextMatch));

        access_oid(vacmAccessReadViewName, &name_length, groupName, prefix,
                   secModel, secLevel);
        snmp_pdu_add_variable(pdu, vacmAccessReadViewName, name_length,
                              ASN_OCTET_STR, (u_char *) argv[arg + 4],
                              strlen(argv[arg + 4]));

        access_oid(vacmAccessWriteViewName, &name_length, groupName,
                   prefix, secModel, secLevel);
        snmp_pdu_add_variable(pdu, vacmAccessWriteViewName, name_length,
                              ASN_OCTET_STR, (u_char *) argv[arg + 5],
                              strlen(argv[arg + 5]));

        access_oid(vacmAccessNotifyViewName, &name_length, groupName,
                   prefix, secModel, secLevel);
        snmp_pdu_add_variable(pdu, vacmAccessNotifyViewName, name_length,
                              ASN_OCTET_STR, (u_char *) argv[arg + 6],
                              strlen(argv[arg + 6]));
    } else if (strcmp(argv[arg], CMD_DELETEAUTH_NAME) == 0)
        /*
         * deleteAuth: delete authAccess entry
         *
         * deleteAuth  GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL AUTHTYPE
         *
         */
    {
        if (++arg + 4 > argc) {
            fprintf(stderr,
                    "You must specify the authAccess entry to delete\n");
            usage();
            goto close_session;
        }

        command = CMD_DELETEAUTH;
        name_length = AUTH_OID_LEN;
        groupName = argv[arg];
        if (arg + 5 == argc)
            prefix = argv[++arg];
        else
            prefix = NULL;

        if (sscanf(argv[arg + 1], "%d", &secModel) == 0) {
            printf("invalid security model\n");
            usage();
            goto close_session;
        }
        if (sscanf(argv[arg + 2], "%d", &secLevel) == 0) {
            printf("invalid security level\n");
            usage();
            goto close_session;
        }
        authtype = argv[arg+3];
        auth_oid(nsVacmRowStatus, &name_length, groupName, prefix,
                   secModel, secLevel, authtype);
        longvar = RS_DESTROY;
        snmp_pdu_add_variable(pdu, nsVacmRowStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));
    } else if (strcmp(argv[arg], CMD_CREATEAUTH_NAME) == 0)
        /*
         * createAuth: create authAccess entry
         *
         * createAuth  GROUPNAME [CONTEXTPREFIX] SECURITYMODEL SECURITYLEVEL AUTHTYPE CONTEXTMATCH VIEWNAME
         *
         */
    {
        if (++arg + 6 > argc) {
            fprintf(stderr,
                    "You must specify the authAccess entry to create\n");
            usage();
            goto close_session;
        }

        command = CMD_CREATEAUTH;
        name_length = AUTH_OID_LEN;
        groupName = argv[arg];
        if (arg + 7 == argc)
            prefix = argv[++arg];
        else
            prefix = NULL;

        if (sscanf(argv[arg + 1], "%d", &secModel) == 0) {
            printf("invalid security model\n");
            usage();
            goto close_session;
        }
        if (sscanf(argv[arg + 2], "%d", &secLevel) == 0) {
            printf("invalid security level\n");
            usage();
            goto close_session;
        }
        authtype = argv[arg+3];
        auth_oid(nsVacmRowStatus, &name_length, groupName, prefix,
                   secModel, secLevel, authtype);
        longvar = RS_CREATEANDGO;
        snmp_pdu_add_variable(pdu, nsVacmRowStatus, name_length,
                              ASN_INTEGER, (u_char *) & longvar,
                              sizeof(longvar));

        auth_oid(nsVacmContextPfx, &name_length, groupName, prefix,
                   secModel, secLevel, authtype);
        if (sscanf(argv[arg + 4], "%d", &contextMatch) == 0) {
            printf("invalid contextMatch\n");
            usage();
            goto close_session;
        }
        snmp_pdu_add_variable(pdu, nsVacmContextPfx, name_length,
                              ASN_INTEGER, (u_char *) & contextMatch,
                              sizeof(contextMatch));

        auth_oid(nsVacmViewName, &name_length, groupName, prefix,
                   secModel, secLevel, authtype);
        snmp_pdu_add_variable(pdu, nsVacmViewName, name_length,
                              ASN_OCTET_STR, (u_char *) argv[arg + 5],
                              strlen(argv[arg + 5]));
    } else {
        printf("Unknown command\n");
        usage();
        goto close_session;
    }

    exitval = 0;

    /*
     * do the request 
     */
    status = snmp_synch_response(ss, pdu, &response);
    if (status == STAT_SUCCESS) {
        if (response) {
            if (response->errstat == SNMP_ERR_NOERROR) {
                fprintf(stderr, "%s\n", successNotes[command - 1]);
            } else {
                fprintf(stderr, "Error in packet.\nReason: %s\n",
                        snmp_errstring(response->errstat));
		if (response->errindex != 0){
		    int count;
		    struct variable_list *vars = response->variables;
		    fprintf(stderr, "Failed object: ");
		    for(count = 1; vars && (count != response->errindex);
			    vars = vars->next_variable, count++)
			;
		    if (vars)
			fprint_objid(stderr, vars->name, vars->name_length);
		    fprintf(stderr, "\n");
		}
                exitval = 2;
            }
        }
    } else if (status == STAT_TIMEOUT) {
        fprintf(stderr, "Timeout: No Response from %s\n",
                session.peername);
        exitval = 1;
    } else {
        snmp_sess_perror("snmpset", ss);
        exitval = 1;
    }

    if (response)
        snmp_free_pdu(response);

close_session:
    snmp_close(ss);

out:
    netsnmp_cleanup_session(&session);
    SOCK_CLEANUP;
    return exitval;
}
