2017-01-06 5 views
0

Was ich jetzt habe, ist DNS-Abfrage über UDP und es funktioniert ordnungsgemäß, aber wenn Nachricht abgeschnitten wird und ich über TCP mit der gleichen Abfrage erneut verbinden muss, kann ich das nicht tun, Hauptproblem ist, dass ich es tue habe Abfrage in Byte-Array und tcp sendet Zeichen.DNS-Abfrage über TCP

Meine Montage von UDP-Abfrage:

String DNS_SERVER_ADDRESS = args[0]; 
    String domain = args[1]; 
    ipAddress = InetAddress.getByName(DNS_SERVER_ADDRESS); 

    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    dos = new DataOutputStream(baos); 

    // *** Build a DNS Request Frame **** 

    // Identifier: A 16-bit identification field generated by the device that creates the DNS query. 
    // It is copied by the server into the response, so it can be used by that device to match that 
    // query to the corresponding reply received from a DNS server. This is used in a manner similar 
    // to how the Identifier field is used in many of the ICMP message types. 
    dos.writeShort(0x1234); 

    // Write Query Flags 
    dos.writeShort(0x0100); 

    // Question Count: Specifies the number of questions in the Question section of the message. 
    dos.writeShort(0x0001); 

    // Answer Record Count: Specifies the number of resource records in the Answer section of the message. 
    dos.writeShort(0x0000); 

    // Authority Record Count: Specifies the number of resource records in the Authority section of 
    // the message. (“NS” stands for “name server”) 
    dos.writeShort(0x0000); 

    // Additional Record Count: Specifies the number of resource records in the Additional section of the message. 
    dos.writeShort(0x0000); 

    // TODO: write query 
    String[] domainParts = domain.split("\\."); 
    System.out.println(domain + " has " + domainParts.length + " parts"); 

    for (String domainPart : domainParts) { 
     System.out.println("Writing: " + domainPart); 
     byte[] domainBytes = domainPart.getBytes("UTF-8"); 
     dos.writeByte(domainBytes.length); 
     dos.write(domainBytes); 
    } 

    // No more parts 
    dos.writeByte(0x00); 

    // QType 0x01 = A (Host Request) 
    if (args.length>2) 
     dos.writeShort(typeEncode(args[2])); 
    else 
     dos.writeShort(0x00ff); // "ANY" as default 

    // QClass 0x01 = IN 
    dos.writeShort(0x0001); 

    dnsFrame = baos.toByteArray(); 

    System.out.println("Sending: " + dnsFrame.length + " bytes"); 
    for (byte aDnsFrame : dnsFrame) { 
     System.out.print("0x" + String.format("%x", aDnsFrame) + " "); 
    } 

    // *** Send DNS Request Frame *** 
    DatagramSocket socket = new DatagramSocket(); 
    DatagramPacket dnsReqPacket = new DatagramPacket(dnsFrame, dnsFrame.length, ipAddress, DNS_SERVER_PORT); 
    socket.send(dnsReqPacket); 
    return socket; 

Wie ich versuche es über TCP zu senden:

Socket echoSocket = null; 
    // strumień do zapisu do serwera 
    Writer out = null; 
    // strumień do odczytu z serwera 
    BufferedReader in = null; 
    // nazwa serwera 
    String hostname=args[0]; 

    try { 
     System.out.println("próba utworzenia gniazda"); 
     echoSocket = new Socket(ipAddress, DNS_SERVER_PORT); 
     System.out.println("próba utworzenia strumienia wyjściowego"); 
     out = new PrintWriter(echoSocket.getOutputStream(), true); 
     OutputStream outputStream = echoSocket.getOutputStream(); 
     System.out.println("próba utworzenia strumienia wejściowego"); 
     in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); 


     ObjectOutputStream os = new ObjectOutputStream(echoSocket.getOutputStream()); 
     os.flush(); 
     ObjectInputStream is = new ObjectInputStream(echoSocket.getInputStream()); 
     os.writeObject(dnsFrame); 
     byte[] temp = (byte[]) is.readObject(); 
    } catch (UnknownHostException e) { 
     System.err.println("Nieznany host: " + hostname + "."); 
     System.exit(1); 
    } catch (IOException e) { 
     System.err.println("Błąd połączenia z " + hostname + "."); 
     System.exit(1); 
    } 


    // zakończenie pracy - pozamykaj strumienie i gniazda 
    out.close(); 
    in.close(); 
    echoSocket.close(); 

ich wireshark verwendet zu lesen, was in TCP sende ich tatsächlich, und es ist nicht das gleiche Als UDP nehme ich an, dass das Problem gerade darin besteht, ein Byte-Array zu senden.

+1

_ "tcp sendet Zeichen" _ - was macht Sie denken, ist das wahr? Ein 'ObjectOutputStream' ist für Java-Objekte gedacht und unterscheidet sich sehr von einem 'DataOutputStream'. Ihre TCP- und UDP-Versionen unterscheiden sich sehr voneinander. –

+0

TCP sendet * Oktette *. Sie sollten 'Reader' und' Writers' nicht verwenden, Sie sollten den gleichen 'DataInput/OutputStream' verwenden wie bei YDP, in der Tat sehr ähnlich, aber mit' Socket.getInput/OutputStream() 'als Basiswert Stream statt die 'ByteArrayInput/OutputStreams.' – EJP

Antwort

3

DNS-TCP-Protokoll ist das gleiche wie seine UDP-Protokoll mit einem Unterschied - die Nachrichten über TCP gesendet werden eine 16-Bit-Ganzzahl in Netzwerk-Byte-Reihenfolge vorangestellt, um die Länge der Nachricht Byte angeben. Dies ist nicht über UDP erforderlich, da die Nachrichtenlängen durch die Größe der Datagramme bestimmt werden.

Per RFC 1035, "Domain-Namen - UMSETZUNG UND DATEN":

4,2. Transport

Der DNS geht davon aus, dass Nachrichten als Datagramme oder in einem Byte-Stream von einer virtuellen Verbindung übertragen werden. Während virtuelle Verbindungen für jede DNS-Aktivität verwendet werden können, werden Datagramme für Abfragen aufgrund ihre niedrigeren Overhead und bessere Leistung bevorzugt. Zonenaktualisierungsaktivitäten müssen virtuelle Verbindungen verwenden, da eine zuverlässige Übertragung erforderlich ist.

Das Internet unterstützt Nameserverzugriffs unter Verwendung von TCP [RFC-793] auf dem Server Port 53 (dezimal) sowie Datagramm Zugriff UDP [RFC-768] auf UDP Port 53 (dezimal).

4.2.1. UDP-Verwendung

Nachrichten, die mit dem UDP-Benutzerserveranschluss 53 (dezimal) gesendet wurden.

Nachrichten, die von UDP übertragen werden, sind auf 512 Byte beschränkt (die IP-Adressen oder UDP werden nicht berücksichtigt). Längere Nachrichten werden abgeschnitten und das TC-Bit wird in der Kopfzeile festgelegt.

UDP ist für Zonenübertragungen nicht akzeptabel, aber für Standardabfragen im Internet wird die Methode empfohlen. Abfragen, die unter Verwendung von UDP gesendet werden, können verloren sein, und daher ist eine Neuübertragungsstrategie erforderlich. Abfragen oder ihre Antworten können vom Netzwerk neu geordnet werden oder indem sie in den Namen Servern verarbeitet werden, so dass Resolver nicht davon abhängen sollten, dass sie in Reihenfolge zurückgegeben werden.

Die optimale UDP Neuübertragung Politik mit Leistung des Internet und die Bedürfnisse des Kunden variieren, aber die folgenden empfohlen:

  • Der Client sollte anderen Servern und Server-Adressen versuchen, bevor eine Abfrage zu wiederholen an eine bestimmte Adresse eines Servers.

  • Das Übertragungsintervall sollte nach Möglichkeit auf vorherigen Statistiken basieren.Eine zu aggressive Weiterverbreitung kann die Reaktionen der Community insgesamt verlangsamen. Abhängig davon, wie gut der Client mit den erwarteten Servern verbunden ist, sollte das minimale Wiederholungsintervall 2-5 Sekunden betragen.

Weitere Vorschläge zur Server-Auswahl und Weiterverbreitung Politik werden kann, in dem Resolver Abschnitt dieses Memo gefunden.

4.2.2. TCP-Verwendung

Nachrichten, die über TCP-Verbindungen gesendet werden, verwenden den Serveranschluss 53 (dezimal). Der Nachricht ist ein Feld mit zwei Byte Länge vorangestellt, das die Länge der Nachricht angibt, mit Ausnahme des Felds mit zwei Byte Länge. Dieses Längenfeld ermöglicht die Low-Level-Verarbeitung, um eine vollständige Nachricht vor Beginn zu sammeln, um es zu analysieren.

Mehrere Verbindungsmanagement-Richtlinien empfohlen:

  • Der Server nicht andere Aktivitäten für TCP-Daten warten blockieren sollte.

  • Der Server sollte mehrere Verbindungen unterstützen.

  • Der Server sollte davon ausgehen, dass der Client das Schließen der Verbindung initiiert, und sollte das Ende der Verbindung so lange hinauszögern, bis alle ausstehenden Clientanforderungen erfüllt wurden.

  • Wenn der Server eine ruhende Verbindung schließen muss, um Ressourcen zurückzugewinnen, sollte er warten, bis die Verbindung für einen Zeitraum in der Größenordnung von zwei Minuten inaktiv war. Insbesondere sollte der Server zulassen, dass die SOA- und AXFR-Anforderungssequenz (die eine Auffrischungsoperation beginnt) in einer einzelnen Verbindung durchgeführt wird. Da der Server sowieso keine Anfragen beantworten kann, kann ein einseitiges Schließen oder Zurücksetzen anstelle eines eleganten Schließens verwendet werden.

Also, alles, was Sie in TCP tun müssen, ist:

  1. schließen Sie Ihre TCP-Socket an den DNS-Server

  2. wenn eine DNS-Abfrage zu senden, erstellen Sie Ihre Nachricht Byte Array genau die gleiche Art und Weise Sie sind in UDP, dann senden Sie die Byte-Array-Länge als 16-Bit-Integer vor dem Senden der Byte-Array selbst.

  3. Lesen Sie beim Lesen einer DNS-Antwort zuerst die 16-Bit-Länge und dann die Anzahl der angegebenen Bytes.

  4. Wiederholen Sie die Schritte 2 bis 3, wie für viele Abfragen erforderlich, die Sie senden müssen.

  5. Schließen Sie die Verbindung, wenn Sie fertig sind.

Siehe auch:

RFC 7766: DNS Transport over TCP - Implementation Requirements

Kurz gesagt, Verwenden Sie KEINE E/A-Klassen, die auf Zeichenfolge Zeichen basieren, Objekte, etc., wie Sie gerade versuchen, zu tun. DNS-Nachrichten sind Binärdaten und sind in UDP und TCP genauso formatiert. Ihr Code zum Generieren und Analysieren von DNS-Nachrichten sollte unabhängig vom verwendeten Transport identisch sein. Das einzige, was anders ist, ist, wie Sie das rohe Nachrichtenbytes senden/empfangen: