Reputation: 43
I am new to Go and am running into a situation that I am unsure how to solve. I am working on some code that takes a DNS packet in raw bytes and returns a struct called DNSPacket.
The struct looks like the following
type DNSPacket struct {
...some fields
Questions []Question
Answers []Answer
...some more fields
}
The issue I am having is with the Answers type which looks like this.
type Answer struct {
Name string
Type int
Class int
TTL uint32
RdLength int
Data []byte
}
Depending on the type of Answer the Data
field must be decoded differently. For example if the Answer is an A
record (Type 1) the data is simply an ipv4 address. However if the Answer is an SRV
record (Type 33) then the data is contains port
, priority
, weight
and target
encoded in the byte slice.
I thought it would be great if I could have a method on Answer called DecodeData()
that returns the correct data depending on the type, however since there is no overriding or inheritance in Go I am unsure how to solve this. I tried using an interface to solve this, but it would not compile. I tried something like
type DNSRecordType interface {
Decode(data []byte)
}
type RecordTypeSRV struct {
target string
...more fields
}
//to 'implement' the DNSRecordType interface
func (record *RecordTypeSRV) Decode(data []byte) {
//do the work to decode appropriately and set
//the fields on the record
}
Then in the Answer method
func (a *Answer) DecodeData() DNSRecordType {
if a.Type === SRVType {
record := RecordTypeSRV{}
record.Decode(a.Data)
return record
}
//do something similar for other record types
}
What would be the correct Go way of having a single Answer type, but be able to return different types of Answer Data depending on their type? Sorry, if this is a completely beginner question as I am still very new to Go.
Thanks!
Upvotes: 2
Views: 926
Reputation: 211
As I know, to return different types, the return param must be an interface. So you can simply declare the function like this:
func (a *Answer) DecodeData() (mode modeType, value interface{}) {}
mode means the value is A record or SRV record, and you can return anything you want with the value field.
The function caller can handle the value depending on mode
If you want the code be more elegant, you can define different value structs for each mode. Then the caller may act as below:
type modeType int
const (
ARecord modeType = 1
SRVRecord modeType = 2
)
switch mode {
case ARecord:
// do something
case SRVRecord:
// do something
}
Upvotes: 1
Reputation: 271
Let me summarize your question.
You have a DNS Packet with the list of Answers. Based on the type of answer you have to process the data in the answer.
type DNSPacket struct {
...some fields
Questions []Question
Answers []Answer
...some more fields
}
type Answer struct {
Name string
Type int
Class int
TTL uint32
RdLength int
Data []byte
}
Answer Let's create an interface that should be implemented to process data.
type PacketProcessor interface {
Process(Answer)
}
Let SRV implements the PacketProcessor
type SRV struct {
...
}
func (s *SRV) Process(a Answer) {
...
}
Your processing logic should be as follows
func (a *Answer) Process() {
var p PacketProcessor
switch a.Type {
case SRVType:
p = &SRV{}
...
//other cases
}
//finally
p.Process(*a)
}
Hope it helps :). There is a Gurgaon based golang community that is always ready to help developers with their problems. You can join the community via slack
Upvotes: 5