net-snmp apiのsnmp_sess_open


snmp_sess_openを使って、open、closeするサンプル。
メモリリークしないように、特に注意して書いたつもり。
自分でテストしたときは、mainの処理をforで10万回ループさせても、
とりあえずメモリリークしなかったので、大丈夫だと思う。思いたい。
（v3をopenする処理、1回だけならいいけど、10万回もループさせると、とても重い。）

```cpp:a.cpp

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

#include <string>
#include <map>

#define OIDSIZE(p) (sizeof(p)/sizeof(oid))

struct snmp_node_param_t {
    std::string host;
    std::string community;
    std::string port;
    std::string target;
    std::string timeout; // 秒指定
    std::string retry;
    std::string version; // 1 or v2c or 3のいずれか。
    std::string username;
    std::string level; // noAuthNoPriv or authNoPriv or authPrivのいずれか。
    std::string auth_protocol; // MD5 or SHAのいずれか。
    std::string auth_key;
    std::string priv_protocol; // AES or DESのいずれか。
    std::string priv_key;
};

struct snmp_sessp_t {
    struct snmp_session session;
    void* sess = nullptr;
};

int sess_open(const snmp_node_param_t &node_param, snmp_sessp_t &sessp);
void sess_close(snmp_sessp_t &sessp);

int sess_open(const snmp_node_param_t &node_param, snmp_sessp_t &sessp) {
    // 呼び出し元で、あらかじめ
    // init_snmp("snmpapp");
    // 呼び出しておくこと。
    // 呼び出さなかった場合、snmpv3の処理は失敗する。
    
    sess_close(sessp);
    snmp_sess_init(&sessp.session);
    
    char hostport[4096];
    char community[4096];
    char security_name[4096];
    char auth_key[4096];
    char priv_key[4096];
    
    sprintf(hostport, "%s:%s", node_param.host.c_str(), node_param.port.c_str());
    sprintf(community, "%s", node_param.community.c_str());
    sprintf(security_name, "%s", node_param.username.c_str());
    sprintf(auth_key, "%s", node_param.auth_key.c_str());
    sprintf(priv_key, "%s", node_param.priv_key.c_str());
    
    sessp.session.securityEngineID = 0;
    sessp.session.securityEngineIDLen = 0;
    
    sessp.session.securityName = 0;
    sessp.session.securityNameLen = 0;
    
    sessp.session.contextEngineID = 0;
    sessp.session.contextEngineIDLen = 0;
    
    sessp.session.contextName = 0;
    sessp.session.contextNameLen = 0;
    
    sessp.session.securityAuthProto = nullptr;
    sessp.session.securityPrivProto = nullptr;
    
    sessp.session.peername = hostport;
    sessp.session.remote_port = static_cast<u_short>(atoi(node_param.port.c_str()));
    sessp.session.timeout = atoi(node_param.timeout.c_str()) * 1000000;
    sessp.session.retries = atoi(node_param.retry.c_str());
    
    if (node_param.version == "3") {
        sessp.session.version = SNMP_VERSION_3;
        sessp.session.securityModel = USM_SEC_MODEL_NUMBER;
        sessp.session.securityName = security_name;
        sessp.session.securityNameLen = node_param.username.size();
        
        bool ap = false;
        bool pp = false;
        
        if (node_param.level == "authNoPriv") {
            sessp.session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
            ap = true;
        } else if (node_param.level == "authPriv") {
            sessp.session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
            ap = true;
            pp = true;
        } else {
            // 謎のレベルを指定してきたら、とりあえずnoAuthNoPriv。
            sessp.session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
        }
        
        if (node_param.auth_protocol == "SHA") {
            sessp.session.securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN);
            sessp.session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
        } else {
            // 謎のauthProtocolを指定してきたら、とりあえずMD5。
            sessp.session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN);
            sessp.session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;            
        }
        
        if (node_param.priv_protocol == "AES") {
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN);
            sessp.session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; 
        } else if (node_param.priv_protocol == "AES128") {
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmAES128PrivProtocol, USM_PRIV_PROTO_AES128_LEN);
            sessp.session.securityPrivProtoLen = USM_PRIV_PROTO_AES128_LEN; 
        } else {
            // 謎のprivProtocolを指定してきたら、とりあえずDES。
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN);
            sessp.session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
        }
        
        if (ap && !node_param.auth_key.empty()) {
            sessp.session.securityAuthKeyLen = USM_AUTH_KU_LEN;
            
            if (
                generate_Ku( 
                    sessp.session.securityAuthProto,
                    sessp.session.securityAuthProtoLen,
                    reinterpret_cast<unsigned char*>(auth_key),
                    node_param.auth_key.size(),
                    sessp.session.securityAuthKey,
                    &sessp.session.securityAuthKeyLen 
                ) != SNMPERR_SUCCESS 
            ) {
                printf("Error generating a key (Ku) from the supplied authentication pass phrase.\n");
            }
        }
        
        if (pp && !node_param.priv_key.empty()) {
            sessp.session.securityPrivKeyLen = USM_PRIV_KU_LEN;
            
            if ( 
                generate_Ku(
                    sessp.session.securityAuthProto,
                    sessp.session.securityAuthProtoLen,
                    reinterpret_cast<unsigned char*>(priv_key),
                    node_param.priv_key.size(),
                    sessp.session.securityPrivKey, 
                    &sessp.session.securityPrivKeyLen 
                ) != SNMPERR_SUCCESS 
            ) {
                printf("Error generating a key (Ku) from the supplied privacy pass phrase.\n");
            }
        } else {
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmNoPrivProtocol, OIDSIZE(usmNoPrivProtocol));
			sessp.session.securityPrivProtoLen = OIDSIZE(usmNoPrivProtocol);
			sessp.session.securityPrivKeyLen = USM_PRIV_KU_LEN;
        }
    } else if (node_param.version == "2c") {
        sessp.session.version = SNMP_VERSION_2c;
        sessp.session.securityModel = SNMP_SEC_MODEL_SNMPv2c;
        sessp.session.community = reinterpret_cast<unsigned char*>(community);
        sessp.session.community_len = node_param.community.size();
    } else {
        sessp.session.version = SNMP_VERSION_1;
        sessp.session.securityModel = SNMP_SEC_MODEL_SNMPv1;
        sessp.session.community = reinterpret_cast<unsigned char*>(community);
        sessp.session.community_len = node_param.community.size();
    }
    
    sessp.sess = snmp_sess_open(&sessp.session);
    
    if (sessp.sess == nullptr) {
        int liberror;
        int syserror;
        char *buf;
        
        snmp_error(&sessp.session, &liberror, &syserror, &buf);
        
        printf("%s\n", buf);
        
        free(buf);
        
        return -1;
    }
    
    return 0;
}

void sess_close(snmp_sessp_t &sessp) {
    if (sessp.sess == nullptr) {
        return;
    }
    
    snmp_sess_close(sessp.sess);
    sessp.sess = nullptr;
    
    // snmp_duplicate_objidで確保した領域は、freeしないとダメっぽい。。。
    free(sessp.session.securityAuthProto);
    free(sessp.session.securityPrivProto);
}

int main(int, char**) {
    init_snmp("snmpapp");
    
    snmp_node_param_t node_param;
    node_param.host = "192.168.0.1";
    node_param.community = "public";
    node_param.port = "161";
    node_param.timeout = "2";
    node_param.retry = "1";
    node_param.version = "3";
    node_param.username = "test1";
    node_param.level = "authPriv";
    node_param.auth_protocol = "MD5";
    node_param.auth_key = "authp1234";
    node_param.priv_protocol = "DES";
    node_param.priv_key = "encp1234";
    
    snmp_sessp_t sessp;
    
    if (sess_open(node_param, sessp)) {
        printf("sess_open fail.\n");
        return 1;
    }
    
    // sess_openが成功したら、sessp.sess（snmp_sess_openの戻り値）を使って、
    // getやwalkの処理ができる。これはまた無駄に長い処理がいるので、
    // 別の機会にｚｚｚ
    // sess_closeを呼ばないと、もれなくメモリリークする。
    // また、ソケットも閉じないので、そのうちソケットが開けなくなる。
    // 繰り返し処理で使うときは忘れないように。
    
    sess_close(sessp);
    
    return 0;
}

```