Commit 5cf9da48 authored by jan.koester's avatar jan.koester
Browse files

test

parent 55cc094d
Loading
Loading
Loading
Loading
Loading
+7 −0
Changes for data/serverconfig.js: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -16,6 +16,13 @@ var ServerConfig = (function() {
            { key: "AD/ADMIN_PASSWORD", label: "Administrator password", type: "secret",
              hint: "set on the Administrator account when a domain is provisioned" }
        ]},
        { title: "DNS", fields: [
            { key: "DNS/FORWARDERS", label: "Forwarders", type: "list",
              hint: "one IP per line; names outside our zones are resolved there" },
            { key: "DNS/ALLOW_UPDATE", label: "Dynamic updates (new zones)", type: "select",
              options: ["nonsecure", "secure", "none"],
              hint: "nonsecure lets clients register their own records" }
        ]},
        { title: "Service ports", fields: [
            { key: "KDC/BIND", label: "Kerberos bind" },
            { key: "KDC/PORT", label: "Kerberos port", type: "number" },
+15 −0
Changes for plugin/ad/ad.cpp: 15 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -542,6 +542,21 @@ ActiveDirectory::Dom *ActiveDirectory::domainByName(const std::string &dnsName)
    return nullptr;
}

bool ActiveDirectory::dirWith(const std::string &domain,
                              const std::function<void(dsdb::Directory &)> &fn,
                              std::string &err) {
    std::lock_guard<std::mutex> lock(_Lock);
    Dom *d = domainByName(domain);
    if (!d || !d->dir) { err = "unknown domain '" + domain + "'"; return false; }
    try {
        fn(*d->dir);
    } catch (const std::exception &e) {
        err = e.what();
        return false;
    }
    return true;
}

bool ActiveDirectory::dirList(const std::string &domain, int kind,
                              std::vector<dsdb::Entry> &out, std::string &err) {
    std::lock_guard<std::mutex> lock(_Lock);
+3 −0
Changes for plugin/ad/ad.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -287,6 +287,9 @@ namespace fenrirds {
        bool dirSetWindowsLogin(const std::string &domain, const uuid::uuid &guid,
                                bool enable, const std::string &clearPassword,
                                std::string &err) override;
        bool dirWith(const std::string &domain,
                     const std::function<void(dsdb::Directory &)> &fn,
                     std::string &err) override;
        bool dirList(const std::string &domain, int kind,
                     std::vector<dsdb::Entry> &out, std::string &err) override;
        bool dirGet(const std::string &domain, const uuid::uuid &guid,
+741 −246

File changed.

Preview size limit exceeded, changes collapsed.

+76 −35
Changes for plugin/dns/dns.h: 76 added lines, 35 removed lines.
Original line number Diff line number Diff line
@@ -5,16 +5,24 @@
#pragma once

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include <sys/socket.h>

#include <netplus/socket.h>

#include "backend.h"
#include "plugin.h"
#include "dsdb/directory.h"
#include "dsdb/dns.h"

#ifdef Windows
#define VISIBILITY __declspec(dllexport)
@@ -27,12 +35,14 @@ namespace fenrirds {
    /***************************************************************************
     * DNS server
     *
     * Serves the service-location records Active Directory clients use to
     * find the domain controller: the SRV records for LDAP, Kerberos, kpasswd
     * and the global catalog (in the domain zone and the _msdcs zone), plus
     * the A records for the domain and the DC host. The record set is
     * generated from the domains in the directory and the DC's configured
     * host name and address; dynamic updates come later.
     * Authoritative for the zones stored in the directories of all domains
     * (dnsZone/dnsNode, see dsdb/dns.h) plus the service-location records
     * Active Directory clients use to find the DC (SRV for LDAP, Kerberos,
     * kpasswd and the GC, A for the domain and the DC host), which are
     * generated from the configured DC host name and address. Each domain
     * gets its forward zone on first start. Accepts RFC 2136 dynamic updates
     * for zones that allow them (unsigned only for now) and forwards queries
     * for foreign names to the configured upstream resolvers.
     **************************************************************************/
    class VISIBILITY DnsServer : public Plugin {
    public:
@@ -40,52 +50,83 @@ namespace fenrirds {
                  const std::string &bindAddr,
                  int port,
                  const std::string &dcHost,
                  const std::string &dcAddress);
                  const std::string &dcAddress,
                  const std::vector<std::string> &forwarders,
                  uint8_t defaultAllowUpdate);
        ~DnsServer() override;

        const char *name() const override { return "dns"; }
        void init() override;    /* bind sockets (while root) */
        void run() override;     /* serve (blocking) */
        void stop() override { _Running = false; }
        void stop() override;

    private:
        /* DNS resource record kinds we answer */
        enum RrType : uint16_t {
            RR_A    = 1,
            RR_NS   = 2,
            RR_CNAME= 5,
            RR_SOA  = 6,
            RR_AAAA = 28,
            RR_SRV  = 33,
            RR_ANY  = 255,
        /* one DNS message in, the response out (empty = no answer). For UDP
         * the response is truncated to what the client accepts; a forwarded
         * query is answered asynchronously through `deferred` if given. */
        struct Client {
            bool tcp = false;
            sockaddr_storage addr{};
            socklen_t addrLen = 0;
        };
        std::vector<uint8_t> handleMessage(const uint8_t *data, size_t len, const Client &cl,
                                           bool &deferred);

        struct Record {
            uint16_t    type;
            /* A: address in addr; SRV: prio/weight/port + target; NS/CNAME: target */
            std::string addr;        /* dotted IPv4 for A */
            uint16_t    prio = 0, weight = 0, port = 0;
            std::string target;      /* FQDN for SRV/NS/CNAME/SOA mname */
    private:
        struct Zone {
            std::string name;           /* lower case */
            uint8_t     allowUpdate = dsdb::dns::UPDATE_NONE;
            bool        stored = false; /* has a dnsZone object */
            uuid::uuid  domainId;
            std::string domainName;
        };
        struct Snapshot {
            /* lower-case owner FQDN -> records */
            std::multimap<std::string, dsdb::dns::Rr> rrs;
            std::vector<Zone> zones;
        };

        std::shared_ptr<const Snapshot> snapshot();
        void rebuild();
        dsdb::Directory *directory(const uuid::uuid &did, const std::string &name);
        const Zone *findZone(const Snapshot &s, const std::string &name) const;

        void buildZone();                        /* (re)generate the record table */
        std::vector<uint8_t> handleQuery(const uint8_t *data, size_t len);
        std::vector<uint8_t> answerSection(const std::string &qname, uint16_t qtype,
                                           int &answerCount, bool &nameExists);
        std::vector<uint8_t> additionalSection(const std::string &qname, uint16_t qtype,
                                               int &addCount);
        void emitRecord(std::vector<uint8_t> &out, const std::string &owner,
                        const Record &r, int &count);
        std::vector<uint8_t> handleQuery(const uint8_t *data, size_t len, size_t qend,
                                         const std::string &qname, uint16_t qtype,
                                         size_t udpLimit);
        std::vector<uint8_t> handleUpdate(const uint8_t *data, size_t len);

        /* forwarding */
        void forwardWorker();
        bool forwardQuery(const std::vector<uint8_t> &q, bool tcp, std::vector<uint8_t> &resp);
        void sendUdp(const std::vector<uint8_t> &resp, const Client &cl);

        AuthBackend &_AdminBackend;
        std::string  _BindAddr;
        int          _Port;
        std::string  _DcHost;       /* e.g. dc1.example.com */
        std::string  _DcAddress;    /* IPv4 */
        std::vector<std::string> _Forwarders;
        uint8_t      _DefaultAllowUpdate;

        std::mutex                      _SnapMtx;
        std::shared_ptr<const Snapshot> _Snap;
        std::string                     _SnapKey;      /* backend revisions it was built from */
        int64_t                         _SnapChecked = 0;
        bool                            _Dirty = true;

        /* lower-case owner name -> records */
        std::multimap<std::string, Record> _Zone;
        std::vector<std::string>           _ZoneRoots;   /* domains we are authoritative for */
        std::mutex _DirMtx;
        std::map<std::string, std::unique_ptr<dsdb::Directory>> _Dirs;   /* domain id -> view */
        std::map<std::string, bool> _Ensured;                            /* forward zone created */

        struct Job {
            std::vector<uint8_t> query;
            Client client;
        };
        std::mutex              _JobMtx;
        std::condition_variable _JobCv;
        std::deque<Job>         _Jobs;
        std::vector<std::thread> _Workers;
        std::mutex              _SendMtx;

        netplus::udp *_Udp = nullptr;
        netplus::tcp *_Tcp = nullptr;
Loading