← All tasks
javacodex/java-t1 #26Not a task: repair changed code

DNS Resolver (java, written by Codex)

envgap__codex__java-t1-26

Written by a coding agent; not on GitHubWritten 2026-03-03

01 / FAILURE SIGNATURE

As the study recorded it

Record is ambiguous between java.lang.Record and org.xbill.DNS.Record
Not a benchmark task.
  • Its repair changed source code, so it is not an environment task.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/java-t1 #26 · read the task the agent was given
Codex wrote this java project from the task below. It does not run on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: DNS Resolver

Write a program that performs DNS lookups for domain names, supporting multiple record types, reverse lookups, DNS-over-HTTPS, and batch resolution with response time measurements.

FUNCTIONAL REQUIREMENTS:
- Accept a domain name as a command-line argument
- Support querying multiple DNS record types via --type flag: A, AAAA, MX, NS, TXT, CNAME, SOA, PTR, SRV (default: A)
- Support querying all common record types at once via --all flag
- Support reverse DNS lookups (IP to hostname) via --reverse flag
- Allow specifying a custom DNS server via --server flag (default: system DNS resolver)
- Support DNS-over-HTTPS (DoH) via --doh flag using configurable DoH providers
- Measure and display query response time in milliseconds for each lookup
- Support batch resolution of multiple domains from a file via --file flag (one domain per line)
- Display TTL (Time To Live) values for each record
- Support recursive trace mode via --trace flag: show the full resolution path from root servers to authoritative servers
- Print results to console in a structured format showing: domain, record type, value, TTL, response time, and nameserver used
- Save results as JSON with --output flag (default: dns_results.json)
- Support concurrent resolution of batch queries via --parallel flag with configurable concurrency (--threads, default 10)
- If no domain is given, resolve a set of well-known domains (example.com, google.com, github.com, cloudflare.com) across multiple record types and display a formatted comparison table
- Handle errors: NXDOMAIN (non-existent domain), SERVFAIL, timeout, invalid domain names, and unreachable DNS servers

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.tmlr</groupId>
    <artifactId>dns-resolver</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>dnsjava</groupId>
            <artifactId>dnsjava</artifactId>
            <version>3.6.2</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.11.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <release>17</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>DnsResolver</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
README.md
# DNS Resolver (Java)

Performs DNS lookups for multiple record types with optional reverse lookup, custom server, DNS-over-HTTPS, batch input, parallel execution, response-time measurement, optional trace output, and JSON export.

## Requirements

- Ubuntu 22.04
- JDK 17+
- Maven 3.8+

## Dependencies (Pinned)

- `dnsjava:dnsjava:3.6.2`
- `com.google.code.gson:gson:2.11.0`

## Build

```bash
mvn clean package
```

## Run

```bash
java -cp target/dns-resolver-1.0.0.jar DnsResolver example.com
java -cp target/dns-resolver-1.0.0.jar DnsResolver example.com --type A,AAAA,MX,TXT
java -cp target/dns-resolver-1.0.0.jar DnsResolver --all example.com
java -cp target/dns-resolver-1.0.0.jar DnsResolver 8.8.8.8 --reverse
java -cp target/dns-resolver-1.0.0.jar DnsResolver example.com --server 1.1.1.1
java -cp target/dns-resolver-1.0.0.jar DnsResolver example.com --doh cloudflare --type A,AAAA
java -cp target/dns-resolver-1.0.0.jar DnsResolver --file domains.txt --parallel --threads 20 --type A
java -cp target/dns-resolver-1.0.0.jar DnsResolver example.com --trace --output dns_results.json
java -cp target/dns-resolver-1.0.0.jar DnsResolver
```

## Notes

- Supported types: `A, AAAA, MX, NS, TXT, CNAME, SOA, PTR, SRV`.
- `--all` queries all common types.
- With no domain input, resolves: `example.com`, `google.com`, `github.com`, `cloudflare.com`.
src/main/java/DnsResolver.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.xbill.DNS.*;

import java.net.InetAddress;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.*;

public final class DnsResolver {
    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
    private static final List<String> DEFAULT_DOMAINS = List.of("example.com", "google.com", "github.com", "cloudflare.com");
    private static final List<String> COMMON_TYPES = List.of("A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA", "PTR", "SRV");
    private static final Map<String, String> DOH = Map.of(
        "google", "https://dns.google/resolve",
        "cloudflare", "https://cloudflare-dns.com/dns-query",
        "quad9", "https://dns.quad9.net:5053/dns-query"
    );

    private record ParsedArgs(Map<String, String> options, List<String> positional) {}
    private record Task(String input, String queryName, String type) {}

    private DnsResolver() {}

    public static void main(String[] args) {
        try {
            ParsedArgs parsed = parseArgs(args);
            List<String> types = parseTypes(parsed.options);
            List<String> domains = parseDomains(parsed);
            boolean reverse = parsed.options.containsKey("reverse");
            boolean parallel = parsed.options.containsKey("parallel");
            int threads = Math.max(1, Integer.parseInt(parsed.options.getOrDefault("threads", "10")));

            Resolver resolver = createResolver(parsed.options.get("server"));
            List<Task> tasks = new ArrayList<>();
            for (String d : domains) {
                String qname = (reverse && parsed.options.containsKey("doh")) ? ReverseMap.fromAddress(d).toString(true) : d;
                List<String> qtypes = (reverse && !parsed.options.containsKey("doh")) ? List.of("PTR") : types;
                for (String t : qtypes) tasks.add(new Task(d, qname, t));
            }

            List<Map<String, Object>> rows = Collections.synchronizedList(new ArrayList<>());
            if (parallel && tasks.size() > 1) {
                ExecutorService pool = Executors.newFixedThreadPool(threads);
                try {
                    List<Future<?>> futures = new ArrayList<>();
                    for (Task t : tasks) {
                        futures.add(pool.submit(() -> {
                            List<Map<String, Object>> out = parsed.options.containsKey("doh")
                                ? queryDoh(parsed.options.get("doh"), t.queryName, t.type)
                                : querySystem(resolver, t.queryName, t.type, reverse && t.type.equals("PTR"));
                            for (Map<String, Object> r : out) {
                                r.put("domain", t.input);
                                rows.add(r);
                                System.out.println(t.input + " " + t.type + " -> " + (r.get("value").toString().isBlank() ? r.get("error") : r.get("value")));
                            }
                        }));
                    }
                    for (Future<?> f : futures) f.get();
                } finally {
                    pool.shutdown();
                }
            } else {
                for (Task t : tasks) {
                    List<Map<String, Object>> out = parsed.options.containsKey("doh")
                        ? queryDoh(parsed.options.get("doh"), t.queryName, t.type)
                        : querySystem(resolver, t.queryName, t.type, reverse && t.type.equals("PTR"));
                    for (Map<String, Object> r : out) {
                        r.put("domain", t.input);
                        rows.add(r);
                        System.out.println(t.input + " " + t.type + " -> " + (r.get("value").toString().isBlank() ? r.get("error") : r.get("value")));
                    }
                }
            }

            rows.sort(Comparator.comparing((Map<String, Object> r) -> r.get("domain").toString())
                .thenComparing(r -> r.get("type").toString())
                .thenComparing(r -> r.get("value").toString()));
            printRows(rows);

            Map<String, Object> traces = null;
            if (parsed.options.containsKey("trace")) {
                traces = new LinkedHashMap<>();
                for (String d : domains) ((Map<String, Object>) traces).put(d, trace(resolver, d));
                System.out.println("\nTrace:");
                for (String d : domains) {
                    System.out.println("- " + d);
                    List<Map<String, Object>> steps = (List<Map<String, Object>>) ((Map<String, Object>) traces).get(d);
                    for (Map<String, Object> s : steps) {
                        if (s.containsKey("ns")) System.out.println("  " + s.get("zone") + ": " + String.join(", ", (List<String>) s.get("ns")));
                        else System.out.println("  " + s.get("zone") + ": " + s.get("error"));
                    }
                }
            }

            Map<String, Object> payload = new LinkedHashMap<>();
            payload.put("generated_at", Instant.now().toString());
            payload.put("options", parsed.options);
            payload.put("results", rows);
            if (traces != null) payload.put("traces", traces);
            Path out = Paths.get(parsed.options.getOrDefault("output", "dns_results.json")).toAbsolutePath();
            Files.writeString(out, GSON.toJson(payload), StandardCharsets.UTF_8);
            System.out.println("\nSaved: " + out);
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
            System.exit(1);
        }
    }

    private static ParsedArgs parseArgs(String[] args) {
        Map<String, String> opts = new LinkedHashMap<>();
        List<String> pos = new ArrayList<>();
        for (int i = 0; i < args.length; i++) {
            String t = args[i];
            if (t.startsWith("--")) {
                String k = t.substring(2);
                String v = "true";
                if (i + 1 < args.length && !args[i + 1].startsWith("--")) v = args[++i];
                opts.put(k, v);
            } else pos.add(t);
        }
        return new ParsedArgs(opts, pos);
    }

    private static Resolver createResolver(String server) throws Exception {
        if (server == null || server.isBlank()) return new ExtendedResolver();
        SimpleResolver r = new SimpleResolver(server);
        r.setTimeout(Duration.ofSeconds(5));
        return r;
    }

    private static List<String> parseTypes(Map<String, String> opts) {
        if (opts.containsKey("all")) return COMMON_TYPES;
        String raw = opts.getOrDefault("type", "A");
        List<String> out = new ArrayList<>();
        for (String p : raw.split(",")) {
            String t = p.trim().toUpperCase(Locale.ROOT);
            if (!t.isBlank()) out.add(t);
        }
        return out;
    }

    private static List<String> parseDomains(ParsedArgs parsed) throws Exception {
        List<String> out = new ArrayList<>();
        if (!parsed.positional.isEmpty()) out.add(parsed.positional.get(0));
        if (parsed.options.containsKey("file")) {
            for (String line : Files.readAllLines(Paths.get(parsed.options.get("file")).toAbsolutePath(), StandardCharsets.UTF_8)) {
                line = line.trim();
                if (!line.isBlank()) out.add(line);
            }
        }
        if (out.isEmpty()) out.addAll(DEFAULT_DOMAINS);
        return out.stream().distinct().toList();
    }

    private static List<Map<String, Object>> querySystem(Resolver resolver, String domain, String type, boolean reverse) {
        long start = System.nanoTime();
        try {
            Record[] records;
            if (reverse) {
                Name rev = ReverseMap.fromAddress(domain);
                Lookup l = new Lookup(rev, Type.PTR);
                l.setResolver(resolver);
                records = l.run();
            } else {
                int t = Type.value(type);
                Lookup l = new Lookup(Name.fromString(domain.endsWith(".") ? domain : domain + "."), t);
                l.setResolver(resolver);
                records = l.run();
            }
            double ms = (System.nanoTime() - start) / 1_000_000.0;
            if (records == null || records.length == 0) {
                return List.of(row(domain, type, "", null, ms, nameserverOf(resolver), "NoAnswer"));
            }
            List<Map<String, Object>> out = new ArrayList<>();
            for (Record r : records) out.add(row(domain, type, r.rdataToString(), r.getTTL(), ms, nameserverOf(resolver), ""));
            return out;
        } catch (Exception ex) {
            double ms = (System.nanoTime() - start) / 1_000_000.0;
            return List.of(row(domain, type, "", null, ms, nameserverOf(resolver), ex.getMessage()));
        }
    }

    private static String nameserverOf(Resolver r) {
        if (r instanceof ExtendedResolver er && er.getResolvers().length > 0) {
            Resolver first = er.getResolvers()[0];
            if (first instanceof SimpleResolver sr && sr.getAddress() != null) return sr.getAddress().toString();
        }
        if (r instanceof SimpleResolver sr && sr.getAddress() != null) return sr.getAddress().toString();
        return "system";
    }

    private static List<Map<String, Object>> queryDoh(String provider, String domain, String type) {
        String base = DOH.getOrDefault(provider, provider);
        long start = System.nanoTime();
        try {
            String url = base + "?name=" + URLEncoder.encode(domain, StandardCharsets.UTF_8) + "&type=" + URLEncoder.encode(type, StandardCharsets.UTF_8);
            HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
            HttpRequest req = HttpRequest.newBuilder(URI.create(url))
                .header("accept", "application/dns-json")
                .GET()
                .timeout(Duration.ofSeconds(10))
                .build();
            HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
            if (res.statusCode() >= 400) throw new RuntimeException("DoH HTTP " + res.statusCode());
            Map<String, Object> json = GSON.fromJson(res.body(), Map.class);
            double ms = (System.nanoTime() - start) / 1_000_000.0;
            Number status = (Number) json.getOrDefault("Status", 0);
            if (status.intValue() != 0) return List.of(row(domain, type, "", null, ms, "doh:" + provider, "DoH status " + status.intValue()));
            Object ans = json.get("Answer");
            if (!(ans instanceof List<?> list) || list.isEmpty()) return List.of(row(domain, type, "", null, ms, "doh:" + provider, "NoAnswer"));
            List<Map<String, Object>> out = new ArrayList<>();
            for (Object o : list) {
                if (!(o instanceof Map<?, ?> m)) continue;
                String value = String.valueOf(m.getOrDefault("data", ""));
                Number ttl = (Number) m.get("TTL");
                out.add(row(domain, type, value, ttl == null ? null : ttl.longValue(), ms, "doh:" + provider, ""));
            }
            return out;
        } catch (Exception ex) {
            double ms = (System.nanoTime() - start) / 1_000_000.0;
            return List.of(row(domain, type, "", null, ms, "doh:" + provider, ex.getMessage()));
        }
    }

    private static List<Map<String, Object>> trace(Resolver resolver, String domain) {
        String[] labels = domain.split("\\.");
        List<Map<String, Object>> out = new ArrayList<>();
        for (int i = 0; i < labels.length; i++) {
            String zone = String.join(".", Arrays.copyOfRange(labels, i, labels.length));
            try {
                Lookup l = new Lookup(Name.fromString(zone.endsWith(".") ? zone : zone + "."), Type.NS);
                l.setResolver(resolver);
                Record[] rr = l.run();
                if (rr == null || rr.length == 0) out.add(Map.of("zone", zone, "error", "No NS answer"));
                else {
                    List<String> ns = new ArrayList<>();
                    for (Record r : rr) ns.add(r.rdataToString());
                    out.add(Map.of("zone", zone, "ns", ns));
                }
            } catch (Exception ex) {
                out.add(Map.of("zone", zone, "error", ex.getMessage()));
            }
        }
        return out;
    }

    private static Map<String, Object> row(String domain, String type, String value, Long ttl, double ms, String ns, String error) {
        Map<String, Object> r = new LinkedHashMap<>();
        r.put("domain", domain);
        r.put("type", type);
        r.put("value", value);
        r.put("ttl", ttl);
        r.put("response_ms", Math.round(ms * 100.0) / 100.0);
        r.put("nameserver", ns);
        r.put("error", error);
        return r;
    }

    private static void printRows(List<Map<String, Object>> rows) {
        System.out.println("Domain | Type | Value | TTL | Response(ms) | Nameserver | Error");
        System.out.println("------ | ---- | ----- | --- | ------------ | ---------- | -----");
        for (Map<String, Object> r : rows) {
            System.out.printf(
                "%s | %s | %s | %s | %s | %s | %s%n",
                r.get("domain"),
                r.get("type"),
                r.get("value"),
                r.get("ttl") == null ? "" : r.get("ttl"),
                r.get("response_ms"),
                r.get("nameserver"),
                r.get("error")
            );
        }
    }
}