1+ const http = require ( 'http' ) ;
2+ const https = require ( 'https' ) ;
3+ const http2 = require ( 'http2' ) ;
14const Packet = require ( '../packet' ) ;
25
3- const defaultGet = url => new Promise ( ( resolve , reject ) => {
4- const headers = {
5- accept : 'application/dns-message' ,
6- } ;
7- const base = url . startsWith ( 'https' ) ? require ( 'https' ) : require ( 'http' ) ;
8- const req = base . get ( url , { headers } , resolve ) ;
9- req . on ( 'error' , reject ) ;
10- } ) ;
6+ const protocols = {
7+ 'http:' : http . get ,
8+ 'https:' : https . get ,
9+ 'h2:' : ( url , options , cb ) => {
10+ const urlObj = new URL ( url ) ;
11+ const client = http2 . connect ( url . replace ( 'h2:' , 'https:' ) ) ;
12+ const req = client . request ( {
13+ ':path' : `${ urlObj . pathname } ${ urlObj . search } ` ,
14+ ':method' : 'GET' ,
15+ ...options . headers
16+ } ) ;
17+
18+ req . on ( 'response' , headers => {
19+ client . close ( ) ;
20+ cb ( {
21+ headers,
22+ statusCode : headers [ ':status' ] ,
23+ on : req . on . bind ( req )
24+ } ) ;
25+ } ) ;
26+
27+ req . on ( 'error' , err => {
28+ client . close ( ) ;
29+ throw err ;
30+ } ) ;
1131
12- const readStream = stream => {
13- const buffer = [ ] ;
14- return new Promise ( ( resolve , reject ) => {
15- stream
16- . on ( 'error' , reject )
17- . on ( 'data' , chunk => buffer . push ( chunk ) )
18- . on ( 'end' , ( ) => resolve ( Buffer . concat ( buffer ) ) ) ;
19- } ) ;
32+ req . end ( ) ;
33+ }
2034} ;
2135
22- /**
23- * @docs https://tools.ietf.org/html/rfc8484
24- * @param {* } param0
25- */
26- const DOHClient = ( { dns, http, get = defaultGet } = { } ) => {
27- return ( name , type = 'A' , cls = Packet . CLASS . IN , { clientIp, recursive = true } = { } ) => {
28- const packet = new Packet ( ) ;
29- // see https://github.com/song940/node-dns/issues/29
30- if ( recursive ) {
31- packet . header . rd = 1 ;
32- }
33- if ( clientIp ) {
34- packet . additionals . push ( Packet . Resource . EDNS ( [
35- Packet . Resource . EDNS . ECS ( clientIp ) ,
36- ] ) ) ;
37- }
38- packet . questions . push ( {
39- name,
40- class : cls ,
41- type : Packet . TYPE [ type ] ,
36+ const makeRequest = ( url , query ) => new Promise ( ( resolve , reject ) => {
37+ const index = url . indexOf ( '://' ) ;
38+ if ( index === - 1 ) url = `https://${ url } ` ;
39+ const u = new URL ( url ) ;
40+ const get = protocols [ u . protocol ] ;
41+ if ( ! get ) throw new Error ( `Unsupported protocol: ${ u . protocol } , must be specified (http://, https:// or h2://)` ) ;
42+ if ( ! u . pathname ) url += '/dns-query?dns={query}' ;
43+ url = url . replace ( '{query}' , query ) ;
44+ const req = get ( url , { headers : { accept : 'application/dns-message' } } , resolve ) ;
45+ if ( req ) req . on ( 'error' , reject ) ;
46+ } ) ;
47+
48+ const readStream = res => new Promise ( ( resolve , reject ) => {
49+ const chunks = [ ] ;
50+ res
51+ . on ( 'error' , reject )
52+ . on ( 'data' , chunk => chunks . push ( chunk ) )
53+ . on ( 'end' , ( ) => {
54+ const data = Buffer . concat ( chunks ) ;
55+ if ( res . statusCode !== 200 ) {
56+ reject ( new Error ( `HTTP ${ res . statusCode } : ${ data . toString ( ) } ` ) ) ;
57+ }
58+ resolve ( data ) ;
4259 } ) ;
43- const query = packet . toBase64URL ( ) ;
44- return Promise . resolve ( get ( `http${ http ? '' : 's' } ://${ dns } /dns-query?dns=${ query } ` ) )
45- . then ( readStream )
46- . then ( Packet . parse ) ;
60+ } ) ;
61+
62+ const buildQuery = ( { name, type = 'A' , cls = Packet . CLASS . IN , clientIp, recursive = true } ) => {
63+ const packet = new Packet ( ) ;
64+ packet . header . rd = recursive ? 1 : 0 ;
65+
66+ if ( clientIp ) {
67+ packet . additionals . push ( Packet . Resource . EDNS ( [
68+ Packet . Resource . EDNS . ECS ( clientIp )
69+ ] ) ) ;
70+ }
71+
72+ packet . questions . push ( { name, class : cls , type : Packet . TYPE [ type ] } ) ;
73+ return packet . toBase64URL ( ) ;
74+ } ;
75+
76+ const DOHClient = ( { dns } ) => {
77+ return async ( name , type , cls , options = { } ) => {
78+ const query = buildQuery ( { name, type, cls, ...options } ) ;
79+ const response = await makeRequest ( dns , query ) ;
80+ const data = await readStream ( response ) ;
81+ return Packet . parse ( data ) ;
4782 } ;
4883} ;
4984
50- module . exports = DOHClient ;
85+ module . exports = DOHClient ;
0 commit comments