Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,30 +400,35 @@ func (e *exchange) unauthenticatedReport(resp []byte, cursor int, reply *SnmpPac
// answers reports whether reply answers the request, and with which error. An
// SNMPv3 request is answered only by an SNMPv3 reply with the msgID of its
// current or an earlier attempt, and an SNMPv3 reply answers no other request
// (RFC 3412 section 7.2 step 10). A Report then answers with its kind's error,
// none for a resync Report; another reply with the request ID of the current
// or an earlier attempt. Known bugs: nothing else of the reply is compared
// with the request (its PDU type, the SNMPv1 and SNMPv2c versions, the
// (RFC 3412 section 7.2 step 10). A Report that reportKindOf maps then answers
// with its kind's error, none for a resync Report; any other reply only as a
// Response (GetResponse), with the request ID of the current or an earlier
// attempt: a request sent back unchanged carries the client's own IDs and
// digest, and other PDUs answer no request. Known bugs: nothing else of the
// reply is compared with the request (the SNMPv1 and SNMPv2c versions, the
// security model, level and context of RFC 3412 section 7.2 step 12 b, nor,
// for a request without authentication, the user and engine ID); an empty
// reply without an error status answers before its request ID is checked;
// request ID 0 answers any request.
// Response without an error status answers before its request ID is checked;
// a Response with request ID 0 answers any request.
func (e *exchange) answers(reply *SnmpPacket, earlier []sentIDs) (bool, error) {
if reply.Version == Version3 || e.packet.Version == Version3 {
if reply.Version != e.packet.Version || !e.sentWithMsgID(reply.MsgID, earlier) {
return false, nil
}
}
if reply.Error == NoError && len(reply.Variables) < 1 {
e.x.Logger.Print("Empty result: the reply answers the request")
return true, nil
}
if kind, ok := reportKindOf(reply); ok {
if kind.resync {
return true, nil
}
return true, kind.err
}
if reply.PDUType != GetResponse {
return false, nil
}
if reply.Error == NoError && len(reply.Variables) < 1 {
e.x.Logger.Print("Empty result: the reply answers the request")
return true, nil
}
return e.sentWithRequestID(reply.RequestID, earlier) || reply.RequestID == 0, nil
}

Expand Down
10 changes: 5 additions & 5 deletions engine_requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ func engineScenarios() map[string]engineScenario {
}
return []agentReply{{data: b}}
},
knownBug: "a reply with request ID 0 is accepted for any request, even a v1 Trap",
},
"answer/empty": {agent: answerWith(nil)},
"answer/empty-wrong-id": {
Expand All @@ -308,12 +307,13 @@ func engineScenarios() map[string]engineScenario {
"answer/empty-with-error-status-wrong-id": {agent: answerWith(func(p *SnmpPacket) { p.RequestID += 100; p.Error = GenErr })},
"answer/error-status": {agent: answerWith(func(p *SnmpPacket) { p.Error, p.ErrorIndex = NoSuchName, 1 }, SnmpPDU{Name: engineOID, Type: Null})},
"answer/not-a-response": {
agent: answerWith(func(p *SnmpPacket) { p.PDUType = GetRequest }, sysDescr),
knownBug: "the reply's PDU type is not checked",
agent: answerWith(func(p *SnmpPacket) { p.PDUType = GetRequest }, sysDescr),
},
"answer/reflected-request": {
agent: func(_ int, req []byte) []agentReply { return []agentReply{{data: bytes.Clone(req)}} },
},
"answer/report": {
agent: answerWith(func(p *SnmpPacket) { p.PDUType = Report }, sysDescr),
knownBug: "the reply's PDU type is not checked",
agent: answerWith(func(p *SnmpPacket) { p.PDUType = Report }, sysDescr),
},
"answer/unknown-pdu-type": {
agent: answerRaw(func(r []byte) []byte {
Expand Down
9 changes: 8 additions & 1 deletion engine_v3_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ type agentAnswer struct {
raw func(*v3Message) // edits the encoded answer, after edit
encode func(v3Message) []byte // encodes the answer after raw, instead of v3Message.bytes
drop bool // no answer
why string
// reflect sends the request back unchanged instead of an answer, as a
// loop or an on-path sender does.
reflect bool
why string
}

const (
Expand Down Expand Up @@ -178,6 +181,10 @@ func (a *fakeV3Agent) handle(n int, data []byte) []agentReply {
a.tr.addf("agent: drops #%d (%s)", n, ans.why)
return nil
}
if ans.reflect {
a.tr.addf("agent: reflects #%d unchanged (%s)", n, ans.why)
return []agentReply{{data: bytes.Clone(data)}}
}

out := a.answer(req, ans)
a.tr.addf("agent: answers #%d with %s at %s (%s)", n, describeAnswerPDU(out), describeFlags(out.MsgFlags), ans.why)
Expand Down
23 changes: 21 additions & 2 deletions engine_v3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,25 @@ func v3Scenarios() map[string]v3Scenario {
"answer/other-version/noauth": {
user: "codec-noauth", script: onRequest(2, agentAnswer{why: "SNMPv2c GetResponse", edit: asV2c}),
},
// A request sent back unchanged carries the client's own msgID,
// request ID and, when authenticated, its own digest and ciphertext.
"answer/reflected-request/noauth": {
user: "codec-noauth", script: onRequest(2, agentAnswer{reflect: true, why: "the request sent back"}),
},
"answer/reflected-request/discovery": {
user: "codec-md5", script: onRequest(1, agentAnswer{reflect: true, why: "the discovery request sent back"}),
},
"answer/reflected-request/md5": {
user: "codec-md5", script: onRequest(2, agentAnswer{reflect: true, why: "the request sent back"}),
},
"answer/reflected-request/md5-set": {
user: "codec-md5", script: onRequest(2, agentAnswer{reflect: true, why: "the request sent back"}),
run: func(x *GoSNMP, _ *fakeV3Agent) (*SnmpPacket, error) { return setEngineOID(x) },
},
"answer/reflected-request/sha-aes-set": {
user: "codec-sha-aes", script: onRequest(2, agentAnswer{reflect: true, why: "the request sent back"}),
run: func(x *GoSNMP, _ *fakeV3Agent) (*SnmpPacket, error) { return setEngineOID(x) },
},
// An SNMPv2c message whose body reads as a scoped PDU, with the
// request's request ID, does not answer an SNMPv3 request.
"answer/other-version/noauth-scoped-body": {
Expand Down Expand Up @@ -1033,19 +1052,19 @@ func v3Scenarios() map[string]v3Scenario {
}),
knownBug: "a reply with another security model is accepted, then returned together with the model mismatch error",
},
// A Report carries one varbind (RFC 3412 section 7.1 step 3 d): one with
// two is read past.
"answer/report-two-varbinds": {
user: "codec-noauth", script: onRequest(2, agentAnswer{
report: usmStatsNotInTimeWindows, why: "Report with two varbinds",
edit: func(p *SnmpPacket) { p.Variables = append(p.Variables, sysDescr) },
}),
knownBug: "a Report with more than one varbind is returned as a successful reply",
},
"answer/report-two-varbinds-error-oid": {
user: "codec-noauth", script: onRequest(2, agentAnswer{
report: usmStatsUnknownUserNames, why: "Report with two varbinds",
edit: func(p *SnmpPacket) { p.Variables = append(p.Variables, sysDescr) },
}),
knownBug: "a Report with more than one varbind is returned as a successful reply",
},

// Reports after the engine is known: whether their engine
Expand Down
110 changes: 98 additions & 12 deletions testdata/engine/requests.golden
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,28 @@
0s hook OnSent
0s read from 127.0.0.2:161: 2c "public" GetRequest id=1001 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
0s hook OnRecv
0s hook OnFinish
0s result: packet 2c "public" GetRequest id=1001 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"], no error
0s client after: retries=2 next request id=1002
0s known bug: the reply's PDU type is not checked
1s read from 127.0.0.2:161: deadline
1s hook OnRetry
1s deadline +1s
1s hook PreSend
1s write #2: 2c "public" GetRequest id=1002 [.1.3.6.1.2.1.1.1.0]
1s agent answers #2 after 0s
1s hook OnSent
1s read from 127.0.0.2:161: 2c "public" GetRequest id=1002 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
1s hook OnRecv
2s read from 127.0.0.2:161: deadline
2s hook OnRetry
2s deadline +1s
2s hook PreSend
2s write #3: 2c "public" GetRequest id=1003 [.1.3.6.1.2.1.1.1.0]
2s agent answers #3 after 0s
2s hook OnSent
2s read from 127.0.0.2:161: 2c "public" GetRequest id=1003 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
2s hook OnRecv
3s read from 127.0.0.2:161: deadline
3s hook OnRetry
3s result: packet nil, error "request timeout (after 2 retries)" *errors.errorString
3s client after: retries=2 next request id=1004

== requests/answer/other-community
0s client: 2c timeout=1s retries=2 exponential=false
Expand Down Expand Up @@ -456,6 +474,38 @@
3s result: packet nil, error "request timeout (after 2 retries)" *errors.errorString
3s client after: retries=2 next request id=1004

== requests/answer/reflected-request
0s client: 2c timeout=1s retries=2 exponential=false
0s deadline +1s
0s hook PreSend
0s write #1: 2c "public" GetRequest id=1001 [.1.3.6.1.2.1.1.1.0]
0s agent answers #1 after 0s
0s hook OnSent
0s read from 127.0.0.2:161: 2c "public" GetRequest id=1001 [.1.3.6.1.2.1.1.1.0]
0s hook OnRecv
1s read from 127.0.0.2:161: deadline
1s hook OnRetry
1s deadline +1s
1s hook PreSend
1s write #2: 2c "public" GetRequest id=1002 [.1.3.6.1.2.1.1.1.0]
1s agent answers #2 after 0s
1s hook OnSent
1s read from 127.0.0.2:161: 2c "public" GetRequest id=1002 [.1.3.6.1.2.1.1.1.0]
1s hook OnRecv
2s read from 127.0.0.2:161: deadline
2s hook OnRetry
2s deadline +1s
2s hook PreSend
2s write #3: 2c "public" GetRequest id=1003 [.1.3.6.1.2.1.1.1.0]
2s agent answers #3 after 0s
2s hook OnSent
2s read from 127.0.0.2:161: 2c "public" GetRequest id=1003 [.1.3.6.1.2.1.1.1.0]
2s hook OnRecv
3s read from 127.0.0.2:161: deadline
3s hook OnRetry
3s result: packet nil, error "request timeout (after 2 retries)" *errors.errorString
3s client after: retries=2 next request id=1004

== requests/answer/report
0s client: 2c timeout=1s retries=2 exponential=false
0s deadline +1s
Expand All @@ -465,10 +515,28 @@
0s hook OnSent
0s read from 127.0.0.2:161: 2c "public" Report id=1001 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
0s hook OnRecv
0s hook OnFinish
0s result: packet 2c "public" Report id=1001 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"], no error
0s client after: retries=2 next request id=1002
0s known bug: the reply's PDU type is not checked
1s read from 127.0.0.2:161: deadline
1s hook OnRetry
1s deadline +1s
1s hook PreSend
1s write #2: 2c "public" GetRequest id=1002 [.1.3.6.1.2.1.1.1.0]
1s agent answers #2 after 0s
1s hook OnSent
1s read from 127.0.0.2:161: 2c "public" Report id=1002 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
1s hook OnRecv
2s read from 127.0.0.2:161: deadline
2s hook OnRetry
2s deadline +1s
2s hook PreSend
2s write #3: 2c "public" GetRequest id=1003 [.1.3.6.1.2.1.1.1.0]
2s agent answers #3 after 0s
2s hook OnSent
2s read from 127.0.0.2:161: 2c "public" Report id=1003 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
2s hook OnRecv
3s read from 127.0.0.2:161: deadline
3s hook OnRetry
3s result: packet nil, error "request timeout (after 2 retries)" *errors.errorString
3s client after: retries=2 next request id=1004

== requests/answer/second-attempt
0s client: 2c timeout=1s retries=2 exponential=false
Expand Down Expand Up @@ -570,10 +638,28 @@
0s hook OnSent
0s read from 127.0.0.2:161: 1 "public" Trap id=0 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
0s hook OnRecv
0s hook OnFinish
0s result: packet 1 "public" Trap id=0 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"], no error
0s client after: retries=2 next request id=1002
0s known bug: a reply with request ID 0 is accepted for any request, even a v1 Trap
1s read from 127.0.0.2:161: deadline
1s hook OnRetry
1s deadline +1s
1s hook PreSend
1s write #2: 2c "public" GetRequest id=1002 [.1.3.6.1.2.1.1.1.0]
1s agent answers #2 after 0s
1s hook OnSent
1s read from 127.0.0.2:161: 1 "public" Trap id=0 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
1s hook OnRecv
2s read from 127.0.0.2:161: deadline
2s hook OnRetry
2s deadline +1s
2s hook PreSend
2s write #3: 2c "public" GetRequest id=1003 [.1.3.6.1.2.1.1.1.0]
2s agent answers #3 after 0s
2s hook OnSent
2s read from 127.0.0.2:161: 1 "public" Trap id=0 [.1.3.6.1.2.1.1.1.0=OctetString:"codec agent"]
2s hook OnRecv
3s read from 127.0.0.2:161: deadline
3s hook OnRetry
3s result: packet nil, error "request timeout (after 2 retries)" *errors.errorString
3s client after: retries=2 next request id=1004

== requests/answer/wrong-id
0s client: 2c timeout=1s retries=2 exponential=false
Expand Down
Loading
Loading