Skip to content

Commit

Permalink
Support for dynamically adding of DNS records
Browse files Browse the repository at this point in the history
Signed-off-by: Balaji Vijayakumar <kuttibalaji.v6@gmail.com>
  • Loading branch information
balajiv113 committed May 10, 2023
1 parent ee06501 commit 56f0aac
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 11 deletions.
37 changes: 37 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,40 @@ func (c *Client) Unexpose(req *types.UnexposeRequest) error {
}
return nil
}

func (c *Client) ListDNS() ([]types.Zone, error) {
res, err := c.client.Get(fmt.Sprintf("%s%s", c.base, "/services/dns/all"))
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", res.StatusCode)
}
dec := json.NewDecoder(res.Body)
var dnsZone []types.Zone
if err := dec.Decode(&dnsZone); err != nil {
return nil, err
}
return dnsZone, nil
}

func (c *Client) AddDNS(req *types.Zone) error {
bin, err := json.Marshal(req)
if err != nil {
return err
}
res, err := c.client.Post(fmt.Sprintf("%s%s", c.base, "/services/dns/add"), "application/json", bytes.NewReader(bin))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
err, readErr := io.ReadAll(res.Body)
if readErr != nil {
return fmt.Errorf("error while reading error message: %v", readErr)
}
return errors.New(strings.TrimSpace(string(err)))
}
return nil
}
42 changes: 38 additions & 4 deletions pkg/services/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package dns

import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"strings"

"github.com/containers/gvisor-tap-vsock/pkg/types"
Expand Down Expand Up @@ -111,13 +113,45 @@ func (h *dnsHandler) addAnswers(m *dns.Msg) {
}
}

func Serve(udpConn net.PacketConn, zones []types.Zone) error {
mux := dns.NewServeMux()
type Server struct {
udpConn net.PacketConn
handler *dnsHandler
}

func New(udpConn net.PacketConn, zones []types.Zone) (*Server, error) {
handler := &dnsHandler{zones: zones}
mux.HandleFunc(".", handler.handle)
return &Server{udpConn: udpConn, handler: handler}, nil
}

func (s *Server) Serve() error {
mux := dns.NewServeMux()
mux.HandleFunc(".", s.handler.handle)
srv := &dns.Server{
PacketConn: udpConn,
PacketConn: s.udpConn,
Handler: mux,
}
return srv.ActivateAndServe()
}

func (s *Server) Mux() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/all", func(w http.ResponseWriter, r *http.Request) {
_ = json.NewEncoder(w).Encode(s.handler.zones)
})

mux.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "post only", http.StatusBadRequest)
return
}
var req types.Zone
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

s.handler.zones = append([]types.Zone{req}, s.handler.zones...)
w.WriteHeader(http.StatusOK)
})
return mux
}
18 changes: 11 additions & 7 deletions pkg/virtualnetwork/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ func addServices(configuration *types.Configuration, s *stack.Stack, ipPool *tap
udpForwarder := forwarder.UDP(s, translation, &natLock)
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)

if err := dnsServer(configuration, s); err != nil {
dnsMux, err := dnsServer(configuration, s)
if err != nil {
return nil, err
}

Expand All @@ -45,6 +46,7 @@ func addServices(configuration *types.Configuration, s *stack.Stack, ipPool *tap
mux := http.NewServeMux()
mux.Handle("/forwarder/", http.StripPrefix("/forwarder", forwarderMux))
mux.Handle("/dhcp/", http.StripPrefix("/dhcp", dhcpMux))
mux.Handle("/dns/", http.StripPrefix("/dns", dnsMux))
return mux, nil
}

Expand All @@ -56,22 +58,24 @@ func parseNATTable(configuration *types.Configuration) map[tcpip.Address]tcpip.A
return translation
}

func dnsServer(configuration *types.Configuration, s *stack.Stack) error {
func dnsServer(configuration *types.Configuration, s *stack.Stack) (http.Handler, error) {
udpConn, err := gonet.DialUDP(s, &tcpip.FullAddress{
NIC: 1,
Addr: tcpip.Address(net.ParseIP(configuration.GatewayIP).To4()),
Port: uint16(53),
}, nil, ipv4.ProtocolNumber)
if err != nil {
return err
return nil, err
}
server, err := dns.New(udpConn, configuration.DNS)
if err != nil {
return nil, err
}

go func() {
if err := dns.Serve(udpConn, configuration.DNS); err != nil {
log.Error(err)
}
log.Error(server.Serve())
}()
return nil
return server.Mux(), nil
}

func dhcpServer(configuration *types.Configuration, s *stack.Stack, ipPool *tap.IPPool) (http.Handler, error) {
Expand Down
61 changes: 61 additions & 0 deletions test/basic_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package e2e

import (
"context"
gvproxyclient "github.com/containers/gvisor-tap-vsock/pkg/client"
"github.com/containers/gvisor-tap-vsock/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"net"
"net/http"
)

var _ = Describe("connectivity", func() {
Expand Down Expand Up @@ -58,4 +63,60 @@ var _ = Describe("dns", func() {
Expect(err).ShouldNot(HaveOccurred())
Expect(string(out)).To(ContainSubstring("Address: 192.168.127.254"))
})

It("should resolve dynamically added dns entry test.dynamic.internal", func() {
client := gvproxyclient.New(&http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial("unix", sock)
},
},
}, "http://base")
err := client.AddDNS(&types.Zone{
Name: "dynamic.internal.",
Records: []types.Record{
{
Name: "test",
IP: net.ParseIP("192.168.127.254"),
},
},
})
Expect(err).ShouldNot(HaveOccurred())

out, err := sshExec("nslookup test.dynamic.internal")

Expect(err).ShouldNot(HaveOccurred())
Expect(string(out)).To(ContainSubstring("Address: 192.168.127.254"))
})

It("should resolve recently added dns entry test.dynamic.internal", func() {
client := gvproxyclient.New(&http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial("unix", sock)
},
},
}, "http://base")
err := client.AddDNS(&types.Zone{
Name: "dynamic.internal.",
Records: []types.Record{
{
Name: "test",
IP: net.ParseIP("192.168.127.254"),
},
},
})
Expect(err).ShouldNot(HaveOccurred())

err = client.AddDNS(&types.Zone{
Name: "dynamic.internal.",
DefaultIP: net.ParseIP("192.168.127.253"),
})
Expect(err).ShouldNot(HaveOccurred())

out, err := sshExec("nslookup test.dynamic.internal")

Expect(err).ShouldNot(HaveOccurred())
Expect(string(out)).To(ContainSubstring("Address: 192.168.127.253"))
})
})

0 comments on commit 56f0aac

Please sign in to comment.