title: Learning Golang using gopacket as a starter
body: <p>I have started to learn golang recently (January). I am trying to reproduce a tool we have internally that is written in Python in Go. </p>
  
  <p>So I have the tool working completely for UDP decoding of DNS, however I have been struggling for a week trying to get TCP based DNS decoding. My objective is to log DNS source, destination, query and answers for every packet that hits our DNS servers. In a similar way to what <a href="http://dnstap.info/" rel="nofollow">dnstap</a> does, however we have an internal solution currently using Python to suit our in-house custom logging and event correlation system.</p>
  
  <pre><code>    func Listen(h *pcap.Handle, c *Config, logger chan&lt;- *dnslog) {
          qType := decodeQuery()
          OpCode := decodeOpCode()
          parser := gopacket.NewDecodingLayerParser(
              layers.LayerTypeEthernet,
              &amp;eth,
              &amp;ip4,
              &amp;ip6,
              &amp;tcp,
              &amp;udp,
              &amp;dns,
              )
  
              decoded := make([]gopacket.LayerType, 0, 10)
  
          for {
              data, _, err := h.ZeroCopyReadPacketData()
              if err != nil {
                  log.Println("Error reading packet data ", err)
                  continue
              }
  
              dnslog := &amp;dnslog{}
  
              err = parser.DecodeLayers(data, &amp;decoded)
              for _, layer := range decoded {
                  switch layer {
                  case layers.LayerTypeIPv4:
                      dnslog.Dst = ip4.DstIP.String()
                      dnslog.Src = ip4.SrcIP.String()
                  case layers.LayerTypeIPv6:
                      dnslog.Dst = ip6.DstIP.String()
                      dnslog.Src = ip6.SrcIP.String()
                  case layers.LayerTypeTCP:
                      dnslog.Srcport = fmt.Sprintf("%d", tcp.SrcPort)
                      dnslog.Dstport = fmt.Sprintf("%d", tcp.DstPort)
                  case layers.LayerTypeUDP:
                      dnslog.Srcport = fmt.Sprintf("%d", udp.SrcPort)
                      dnslog.Dstport = fmt.Sprintf("%d", udp.DstPort)
                  case layers.LayerTypeDNS:
                      dnslog.Truncated = dns.TC
                      for _, q := range dns.Questions {
                          dnslog.OpCode = OpCode[uint8(dns.OpCode)]
                          dnslog.QueryCount = dns.QDCount
                          dnslog.AnswerCount = dns.ANCount
                          }
                      }
              }
          logger &lt;- dnslog
      }
  </code></pre>
  
  <p>I have attempted to force the NextLayerType in layers/tcp.go to the DNS Layertype etc in an attempt to find what I am missing. So far no luck. Any advice would be ace. What we see with UDP is the following output. (pprint json encoded output)</p>
  
  <p><code>
  [{
      "src": "172.10.56.23",
      "src_port": "52464",
      "dst": "172.10.16.120",
      "dst_port": "53",
      "bytes": 63,
      "transport": "UDP",
      "reply_code": "Query",
      "query_count": 1,
      "answer_count": 0,
      "question": ["helposx.apple.com"],
      "query_type": "A",
      "answer": null,
      "truncated": false
  }, {
      "src": "172.10.16.120",
      "src_port": "53",
      "dst": "172.10.56.23",
      "dst_port": "52464",
      "bytes": 156,
      "transport": "UDP",
      "reply_code": "Query",
      "query_count": 1,
      "answer_count": 3,
      "question": ["helposx.apple.com"],
      "query_type": "A",
      "answer": [{
          "response-name": "helposx.apple.com",
          "response-query_type": "CNAME",
          "response-ttl": 4607,
          "response-bytes": 31,
          "response-cname": "helposx.apple.com.edgekey.net",
          "response-soa": {},
          "response-srv": {},
          "response-mx": {}
      }, {
          "response-name": "helposx.apple.com.edgekey.net",
          "response-query_type": "CNAME",
          "response-ttl": 33,
          "response-bytes": 22,
          "response-cname": "e3167.e9.akamaiedge.net",
          "response-soa": {},
          "response-srv": {},
          "response-mx": {}
      }, {
          "response-name": "e3167.e9.akamaiedge.net",
          "response-query_type": "A",
          "response-ttl": 13,
          "response-bytes": 4,
          "response-ip": "104.98.20.77",
          "response-soa": {},
          "response-srv": {},
          "response-mx": {}
      }],
      "truncated": false
  }]
  </code></p>
  
  <p>If I now do exactly the same query using dig +tcp (forces TCP) I get the following output.</p>
  
  <p><code>[{
      "src": "172.10.56.23",
      "src_port": "57188",
      "dst": "172.10.16.120",
      "dst_port": "53",
      "bytes": 64,
      "transport": "TCP",
      "reply_code": "",
      "query_count": 0,
      "answer_count": 0,
      "question": null,
      "query_type": "",
      "answer": null,
      "truncated": false
  }, {
      "src": "172.10.16.120",
      "src_port": "53",
      "dst": "172.10.56.23",
      "dst_port": "57188",
      "bytes": 60,
      "transport": "TCP",
      "reply_code": "",
      "query_count": 0,
      "answer_count": 0,
      "question": null,
      "query_type": "",
      "answer": null,
      "truncated": false
  }, {
      "src": "172.10.56.23",
      "src_port": "57188",
      "dst": "172.10.16.120",
      "dst_port": "53",
      "bytes": 52,
      "transport": "TCP",
      "reply_code": "",
      "query_count": 0,
      "answer_count": 0,
      "question": null,
      "query_type": "",
      "answer": null,
      "truncated": false
  }, {
      "src": "172.10.56.23",
      "src_port": "57188",
      "dst": "172.10.16.120",
      "dst_port": "53",
      "bytes": 86,
      "transport": "TCP",
      "reply_code": "",
      "query_count": 0,
      "answer_count": 0,
      "question": null,
      "query_type": "",
      "answer": null,
      "truncated": false
  }, {
      "src": "172.10.16.120",
      "src_port": "53",
      "dst": "172.10.56.23",
      "dst_port": "57188",
      "bytes": 102,
      "transport": "TCP",
      "reply_code": "",
      "query_count": 0,
      "answer_count": 0,
      "question": null,
      "query_type": "",
      "answer": null,
      "truncated": false
  }, {
      "src": "172.10.56.23",
      "src_port": "57188",
      "dst": "172.10.16.120",
      "dst_port": "53",
      "bytes": 52,
      "transport": "TCP",
      "reply_code": "",
      "query_count": 0,
      "answer_count": 0,
      "question": null,
      "query_type": "",
      "answer": null,
      "truncated": false
  }]
  </code>
  From looking at the same packets in wireshark I can see that those various packets are the TCP handshake, then a response. Which is not decoded. </p>
  
  <p>When I add in a fmt.Println(layer) after the for _, layer := range decoded line I get the following.</p>
  
  <p>Ethernet
  IPv4
  TCP
  &lt;&lt; JSON OUTPUT ABOVE.</p>
  
  <p>VS</p>
  
  <p>Ethernet
  IPv4
  UDP
  DNS</p>
  
  <p>As you can see there is never a next decoder for the TCP based DNS. It just stops at TCP. I am unsure as to what the solution is. Reading the upstream library looks like it should work. However it does not and I am confused to where I should be looking. Being new to Go it is sending me in loops. </p>