LinuxSuRen / LinuxSuRen/api-testing

Support to provide a SNMP mock server

Ouverte
#603 3 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Langage dominant
Go
Étoiles
369
Forks
67
Métriques de merge des PR
Aucune PR mergée en 30 j

Description

Below is a sample code:

package main

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/gosnmp/gosnmp"
)

const (
	// SNMP 版本号
	SNMPVersion = 1

	// 固定的 OID 和值
	OID   = ".1.3.6.1.4.1.5105.100.1.9.4.15.0" // sysDescr
	Value = "My SNMP Server"
)

// sendSNMPRequest 发送 SNMP 请求并接收响应
func sendSNMPRequest(target string, community string, oid string) ([]byte, error) {
	// 创建 SNMP 客户端
	gosnmp.Default.Target = target
	gosnmp.Default.Port = 161
	gosnmp.Default.Community = community
	gosnmp.Default.Version = gosnmp.Version2c

	// 创建 PDU
	pdu := gosnmp.SnmpPDU{
		Name:  oid,
		Type:  gosnmp.OctetString,
		Value: Value,
	}

	// 创建请求
	request := &gosnmp.SnmpPacket{
		Version:    gosnmp.Version2c,
		Community:  community,
		Error:      gosnmp.NoError,
		ErrorIndex: 0,
		PDUType:    gosnmp.GetRequest,
		Variables:  []gosnmp.SnmpPDU{pdu},
	}

	// 编码请求
	req, err := request.MarshalMsg()
	if err != nil {
		return nil, fmt.Errorf("failed to marshal SNMP request: %v", err)
	}

	// 发送请求并接收响应
	conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", target, 161))
	if err != nil {
		return nil, fmt.Errorf("failed to dial UDP: %v", err)
	}
	defer conn.Close()

	_, err = conn.Write(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send SNMP request: %v", err)
	}

	buffer := make([]byte, 4096) // Increased buffer size
	n, err := conn.Read(buffer)
	if err != nil {
		return nil, fmt.Errorf("failed to read SNMP response: %v", err)
	}

	return buffer[:n], nil
}

func main() {
	pc, err := net.ListenPacket("udp", ":161")
	if err != nil {
		fmt.Println("Failed to listen on UDP port 161:", err)
		return
	}
	defer pc.Close()

	fmt.Println("SNMP server listening on UDP port 161...")

	buffer := make([]byte, 4096) // Increased buffer size
	for {
		// 读取客户端请求
		n, addr, err := pc.ReadFrom(buffer)
		if err != nil {
			fmt.Println("Failed to read UDP packet:", err)
			continue
		}

		// 解析 SNMP 请求
		req := buffer[:n]
		fmt.Printf("Received SNMP request from %s: %X\n", addr, req)

		// 生成 SNMP 响应
		resp := generateSNMPResponse(req)
		if resp == nil {
			fmt.Println("Failed to generate SNMP response")
			continue
		}

		// 发送响应
		_, err = pc.WriteTo(resp, addr)
		if err != nil {
			fmt.Println("Failed to send SNMP response:", err)
			continue
		} else {
			fmt.Printf("Sent SNMP response to %s: %X\n", addr, resp)
		}
	}

	// 测试发送 SNMP 请求
	//resp, err := sendSNMPRequest("192.168.12.109", "public", ".1.3.6.1.4.1.5105.100.1.9.4.15.0")
	//if err != nil {
	//	fmt.Println("Failed to send SNMP request:", err)
	//} else {
	//	fmt.Printf("Received SNMP response: %X\n", resp)
	//}
}

// generateSNMPResponse 生成 SNMP 响应数据
func generateSNMPResponse(req []byte) []byte {
	// 解析请求
	// 这里仅处理简单的 GET 请求
	if len(req) < 4 {
		fmt.Println("Invalid SNMP request length")
		return nil
	}

	// 提取社区字符串
	communityLen := req[6]
	community := string(req[7 : 7+communityLen]) // 提取社区字符串
	fmt.Printf("Received SNMP request with community: %s\n", community)

	// 提取请求 ID
	requestId := binary.BigEndian.Uint32(req[7+communityLen+2+2 : 7+communityLen+2+2+4])
	fmt.Printf("Received SNMP request with ID %d\n", requestId)

	// 使用 gosnmp 库构建响应
	gosnmp.Default.Target = "192.168.123.58"
	gosnmp.Default.Port = 161
	gosnmp.Default.Community = community
	gosnmp.Default.Version = gosnmp.Version2c

	// 创建 PDU
	pdu := gosnmp.SnmpPDU{
		Name:  OID,
		Type:  gosnmp.OctetString,
		Value: Value,
	}

	// 创建响应
	response := &gosnmp.SnmpPacket{
		Version:   gosnmp.Version2c,
		Community: community,
		//ErrorStatus: gosnmp.NoError,
		Error:      gosnmp.NoError,
		ErrorIndex: 0,
		RequestID:  requestId,
		PDUType:    gosnmp.GetResponse,
		Variables:  []gosnmp.SnmpPDU{pdu},
	}

	// 编码响应
	resp, err := response.MarshalMsg()
	if err != nil {
		fmt.Println("Failed to marshal SNMP response:", err)
		return nil
	}

	fmt.Printf("Generated SNMP response: %X\n", resp)
	return resp
}

// encodeOID 编码 OID
func encodeOID(oid string) []byte {
	// 使用 BER 编码来编码 OID
	parts := splitOID(oid)
	encoded := make([]byte, 0)
	first := parts[0]*40 + parts[1]
	encoded = append(encoded, byte(first))
	for _, part := range parts[2:] {
		encoded = append(encoded, encodeVarInt(part)...)
	}
	return encoded
}

// encodeVarInt 使用 BER 编码来编码一个整数
func encodeVarInt(value int) []byte {
	if value < 128 {
		return []byte{byte(value)}
	}
	encoded := make([]byte, 0)
	for value > 0 {
		encoded = append([]byte{byte(value%128 | 0x80)}, encoded...)
		value /= 128
	}
	encoded[len(encoded)-1] &= 0x7F
	return encoded
}

// encodeValue 编码值
func encodeValue(value string) []byte {
	// 使用 BER 编码来编码值
	encoded := make([]byte, 0)
	encoded = append(encoded, byte(0x04))       // 字符串类型
	encoded = append(encoded, byte(len(value))) // 字符串长度
	encoded = append(encoded, []byte(value)...) // 字符串值
	return encoded
}

// splitOID 分割 OID 字符串为整数数组
func splitOID(oid string) []int {
	parts := make([]int, 0)
	for _, part := range strings.Split(oid, ".") {
		num, _ := strconv.Atoi(part)
		parts = append(parts, num)
	}
	return parts
}

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par localiser les points d’entrée du mock-server du dépôt et les gestionnaires de protocole existants, puis comparez leur structure avec les fonctions main et generateSNMPResponse de l’exemple. Déterminez comment les requêtes et les réponses SNMP doivent s’intégrer à la configuration du mock et aux tests existants. La tâche est terminée lorsqu’un chemin documenté et testable pour un mock-server SNMP gère le comportement demandé sans dépendre des adresses codées en dur de l’exemple.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
go
Domaine
api, testing
Type d'issue
Fonctionnalité
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Plutôt claire
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.