Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pycares/__init__.py: 21%

590 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:52 +0000

1 

2from ._cares import ffi as _ffi, lib as _lib 

3import _cffi_backend # hint for bundler tools 

4 

5if _lib.ARES_SUCCESS != _lib.ares_library_init(_lib.ARES_LIB_INIT_ALL): 

6 raise RuntimeError('Could not initialize c-ares') 

7 

8from . import errno 

9from .utils import ascii_bytes, maybe_str, parse_name 

10from ._version import __version__ 

11 

12import collections.abc 

13import socket 

14import math 

15import functools 

16import sys 

17 

18 

19exported_pycares_symbols = [ 

20 # Flag values 

21 'ARES_FLAG_USEVC', 

22 'ARES_FLAG_PRIMARY', 

23 'ARES_FLAG_IGNTC', 

24 'ARES_FLAG_NORECURSE', 

25 'ARES_FLAG_STAYOPEN', 

26 'ARES_FLAG_NOSEARCH', 

27 'ARES_FLAG_NOALIASES', 

28 'ARES_FLAG_NOCHECKRESP', 

29 

30 # Nameinfo flag values 

31 'ARES_NI_NOFQDN', 

32 'ARES_NI_NUMERICHOST', 

33 'ARES_NI_NAMEREQD', 

34 'ARES_NI_NUMERICSERV', 

35 'ARES_NI_DGRAM', 

36 'ARES_NI_TCP', 

37 'ARES_NI_UDP', 

38 'ARES_NI_SCTP', 

39 'ARES_NI_DCCP', 

40 'ARES_NI_NUMERICSCOPE', 

41 'ARES_NI_LOOKUPHOST', 

42 'ARES_NI_LOOKUPSERVICE', 

43 'ARES_NI_IDN', 

44 'ARES_NI_IDN_ALLOW_UNASSIGNED', 

45 'ARES_NI_IDN_USE_STD3_ASCII_RULES', 

46 

47 # Bad socket 

48 'ARES_SOCKET_BAD', 

49] 

50 

51for symbol in exported_pycares_symbols: 

52 globals()[symbol] = getattr(_lib, symbol) 

53 

54 

55exported_pycares_symbols_map = { 

56 # Query types 

57 "QUERY_TYPE_A" : "T_A", 

58 "QUERY_TYPE_AAAA" : "T_AAAA", 

59 "QUERY_TYPE_ANY" : "T_ANY", 

60 "QUERY_TYPE_CAA" : "T_CAA", 

61 "QUERY_TYPE_CNAME" : "T_CNAME", 

62 "QUERY_TYPE_MX" : "T_MX", 

63 "QUERY_TYPE_NAPTR" : "T_NAPTR", 

64 "QUERY_TYPE_NS" : "T_NS", 

65 "QUERY_TYPE_PTR" : "T_PTR", 

66 "QUERY_TYPE_SOA" : "T_SOA", 

67 "QUERY_TYPE_SRV" : "T_SRV", 

68 "QUERY_TYPE_TXT" : "T_TXT", 

69 

70 # Query classes 

71 "QUERY_CLASS_IN" : "C_IN", 

72 "QUERY_CLASS_CHAOS": "C_CHAOS", 

73 "QUERY_CLASS_HS" : "C_HS", 

74 "QUERY_CLASS_NONE" :"C_NONE", 

75 "QUERY_CLASS_ANY" : "C_ANY", 

76} 

77 

78for k, v in exported_pycares_symbols_map.items(): 

79 globals()[k] = getattr(_lib, v) 

80 

81 

82globals()['ARES_VERSION'] = maybe_str(_ffi.string(_lib.ares_version(_ffi.NULL))) 

83 

84 

85PYCARES_ADDRTTL_SIZE = 256 

86 

87 

88class AresError(Exception): 

89 pass 

90 

91 

92# callback helpers 

93 

94_global_set = set() 

95 

96@_ffi.def_extern() 

97def _sock_state_cb(data, socket_fd, readable, writable): 

98 sock_state_cb = _ffi.from_handle(data) 

99 sock_state_cb(socket_fd, readable, writable) 

100 

101@_ffi.def_extern() 

102def _host_cb(arg, status, timeouts, hostent): 

103 callback = _ffi.from_handle(arg) 

104 _global_set.discard(arg) 

105 

106 if status != _lib.ARES_SUCCESS: 

107 result = None 

108 else: 

109 result = ares_host_result(hostent) 

110 status = None 

111 

112 callback(result, status) 

113 

114@_ffi.def_extern() 

115def _nameinfo_cb(arg, status, timeouts, node, service): 

116 callback = _ffi.from_handle(arg) 

117 _global_set.discard(arg) 

118 

119 if status != _lib.ARES_SUCCESS: 

120 result = None 

121 else: 

122 result = ares_nameinfo_result(node, service) 

123 status = None 

124 

125 callback(result, status) 

126 

127@_ffi.def_extern() 

128def _query_cb(arg, status, timeouts, abuf, alen): 

129 callback, query_type = _ffi.from_handle(arg) 

130 _global_set.discard(arg) 

131 

132 if status == _lib.ARES_SUCCESS: 

133 if query_type == _lib.T_ANY: 

134 result = [] 

135 for qtype in (_lib.T_A, _lib.T_AAAA, _lib.T_CAA, _lib.T_CNAME, _lib.T_MX, _lib.T_NAPTR, _lib.T_NS, _lib.T_PTR, _lib.T_SOA, _lib.T_SRV, _lib.T_TXT): 

136 r, status = parse_result(qtype, abuf, alen) 

137 if status not in (None, _lib.ARES_ENODATA, _lib.ARES_EBADRESP): 

138 result = None 

139 break 

140 if r is not None: 

141 if isinstance(r, collections.abc.Iterable): 

142 result.extend(r) 

143 else: 

144 result.append(r) 

145 else: 

146 status = None 

147 else: 

148 result, status = parse_result(query_type, abuf, alen) 

149 else: 

150 result = None 

151 

152 callback(result, status) 

153 

154@_ffi.def_extern() 

155def _addrinfo_cb(arg, status, timeouts, res): 

156 callback = _ffi.from_handle(arg) 

157 _global_set.discard(arg) 

158 

159 if status != _lib.ARES_SUCCESS: 

160 result = None 

161 else: 

162 result = ares_addrinfo_result(res) 

163 status = None 

164 

165 callback(result, status) 

166 

167def parse_result(query_type, abuf, alen): 

168 if query_type == _lib.T_A: 

169 addrttls = _ffi.new("struct ares_addrttl[]", PYCARES_ADDRTTL_SIZE) 

170 naddrttls = _ffi.new("int*", PYCARES_ADDRTTL_SIZE) 

171 parse_status = _lib.ares_parse_a_reply(abuf, alen, _ffi.NULL, addrttls, naddrttls) 

172 if parse_status != _lib.ARES_SUCCESS: 

173 result = None 

174 status = parse_status 

175 else: 

176 result = [ares_query_a_result(addrttls[i]) for i in range(naddrttls[0])] 

177 status = None 

178 elif query_type == _lib.T_AAAA: 

179 addrttls = _ffi.new("struct ares_addr6ttl[]", PYCARES_ADDRTTL_SIZE) 

180 naddrttls = _ffi.new("int*", PYCARES_ADDRTTL_SIZE) 

181 parse_status = _lib.ares_parse_aaaa_reply(abuf, alen, _ffi.NULL, addrttls, naddrttls) 

182 if parse_status != _lib.ARES_SUCCESS: 

183 result = None 

184 status = parse_status 

185 else: 

186 result = [ares_query_aaaa_result(addrttls[i]) for i in range(naddrttls[0])] 

187 status = None 

188 elif query_type == _lib.T_CAA: 

189 caa_reply = _ffi.new("struct ares_caa_reply **") 

190 parse_status = _lib.ares_parse_caa_reply(abuf, alen, caa_reply) 

191 if parse_status != _lib.ARES_SUCCESS: 

192 result = None 

193 status = parse_status 

194 else: 

195 result = [] 

196 caa_reply_ptr = caa_reply[0] 

197 while caa_reply_ptr != _ffi.NULL: 

198 result.append(ares_query_caa_result(caa_reply_ptr)) 

199 caa_reply_ptr = caa_reply_ptr.next 

200 _lib.ares_free_data(caa_reply[0]) 

201 status = None 

202 elif query_type == _lib.T_CNAME: 

203 host = _ffi.new("struct hostent **") 

204 parse_status = _lib.ares_parse_a_reply(abuf, alen, host, _ffi.NULL, _ffi.NULL) 

205 if parse_status != _lib.ARES_SUCCESS: 

206 result = None 

207 status = parse_status 

208 else: 

209 result = ares_query_cname_result(host[0]) 

210 _lib.ares_free_hostent(host[0]) 

211 status = None 

212 elif query_type == _lib.T_MX: 

213 mx_reply = _ffi.new("struct ares_mx_reply **") 

214 parse_status = _lib.ares_parse_mx_reply(abuf, alen, mx_reply) 

215 if parse_status != _lib.ARES_SUCCESS: 

216 result = None 

217 status = parse_status 

218 else: 

219 result = [] 

220 mx_reply_ptr = mx_reply[0] 

221 while mx_reply_ptr != _ffi.NULL: 

222 result.append(ares_query_mx_result(mx_reply_ptr)) 

223 mx_reply_ptr = mx_reply_ptr.next 

224 _lib.ares_free_data(mx_reply[0]) 

225 status = None 

226 elif query_type == _lib.T_NAPTR: 

227 naptr_reply = _ffi.new("struct ares_naptr_reply **") 

228 parse_status = _lib.ares_parse_naptr_reply(abuf, alen, naptr_reply) 

229 if parse_status != _lib.ARES_SUCCESS: 

230 result = None 

231 status = parse_status 

232 else: 

233 result = [] 

234 naptr_reply_ptr = naptr_reply[0] 

235 while naptr_reply_ptr != _ffi.NULL: 

236 result.append(ares_query_naptr_result(naptr_reply_ptr)) 

237 naptr_reply_ptr = naptr_reply_ptr.next 

238 _lib.ares_free_data(naptr_reply[0]) 

239 status = None 

240 elif query_type == _lib.T_NS: 

241 hostent = _ffi.new("struct hostent **") 

242 parse_status = _lib.ares_parse_ns_reply(abuf, alen, hostent) 

243 if parse_status != _lib.ARES_SUCCESS: 

244 result = None 

245 status = parse_status 

246 else: 

247 result = [] 

248 host = hostent[0] 

249 i = 0 

250 while host.h_aliases[i] != _ffi.NULL: 

251 result.append(ares_query_ns_result(host.h_aliases[i])) 

252 i += 1 

253 _lib.ares_free_hostent(host) 

254 status = None 

255 elif query_type == _lib.T_PTR: 

256 hostent = _ffi.new("struct hostent **") 

257 parse_status = _lib.ares_parse_ptr_reply(abuf, alen, _ffi.NULL, 0, socket.AF_UNSPEC, hostent) 

258 if parse_status != _lib.ARES_SUCCESS: 

259 result = None 

260 status = parse_status 

261 else: 

262 aliases = [] 

263 host = hostent[0] 

264 i = 0 

265 while host.h_aliases[i] != _ffi.NULL: 

266 aliases.append(maybe_str(_ffi.string(host.h_aliases[i]))) 

267 i += 1 

268 result = ares_query_ptr_result(host, aliases) 

269 _lib.ares_free_hostent(host) 

270 status = None 

271 elif query_type == _lib.T_SOA: 

272 soa_reply = _ffi.new("struct ares_soa_reply **") 

273 parse_status = _lib.ares_parse_soa_reply(abuf, alen, soa_reply) 

274 if parse_status != _lib.ARES_SUCCESS: 

275 result = None 

276 status = parse_status 

277 else: 

278 result = ares_query_soa_result(soa_reply[0]) 

279 _lib.ares_free_data(soa_reply[0]) 

280 status = None 

281 elif query_type == _lib.T_SRV: 

282 srv_reply = _ffi.new("struct ares_srv_reply **") 

283 parse_status = _lib.ares_parse_srv_reply(abuf, alen, srv_reply) 

284 if parse_status != _lib.ARES_SUCCESS: 

285 result = None 

286 status = parse_status 

287 else: 

288 result = [] 

289 srv_reply_ptr = srv_reply[0] 

290 while srv_reply_ptr != _ffi.NULL: 

291 result.append(ares_query_srv_result(srv_reply_ptr)) 

292 srv_reply_ptr = srv_reply_ptr.next 

293 _lib.ares_free_data(srv_reply[0]) 

294 status = None 

295 elif query_type == _lib.T_TXT: 

296 txt_reply = _ffi.new("struct ares_txt_ext **") 

297 parse_status = _lib.ares_parse_txt_reply_ext(abuf, alen, txt_reply) 

298 if parse_status != _lib.ARES_SUCCESS: 

299 result = None 

300 status = parse_status 

301 else: 

302 result = [] 

303 txt_reply_ptr = txt_reply[0] 

304 tmp_obj = None 

305 while True: 

306 if txt_reply_ptr == _ffi.NULL: 

307 if tmp_obj is not None: 

308 result.append(ares_query_txt_result(tmp_obj)) 

309 break 

310 if txt_reply_ptr.record_start == 1: 

311 if tmp_obj is not None: 

312 result.append(ares_query_txt_result(tmp_obj)) 

313 tmp_obj = ares_query_txt_result_chunk(txt_reply_ptr) 

314 else: 

315 new_chunk = ares_query_txt_result_chunk(txt_reply_ptr) 

316 tmp_obj.text += new_chunk.text 

317 txt_reply_ptr = txt_reply_ptr.next 

318 _lib.ares_free_data(txt_reply[0]) 

319 status = None 

320 else: 

321 raise ValueError("invalid query type specified") 

322 

323 return result, status 

324 

325 

326class Channel: 

327 __qtypes__ = (_lib.T_A, _lib.T_AAAA, _lib.T_ANY, _lib.T_CAA, _lib.T_CNAME, _lib.T_MX, _lib.T_NAPTR, _lib.T_NS, _lib.T_PTR, _lib.T_SOA, _lib.T_SRV, _lib.T_TXT) 

328 __qclasses__ = (_lib.C_IN, _lib.C_CHAOS, _lib.C_HS, _lib.C_NONE, _lib.C_ANY) 

329 

330 def __init__(self, 

331 flags = None, 

332 timeout = None, 

333 tries = None, 

334 ndots = None, 

335 tcp_port = None, 

336 udp_port = None, 

337 servers = None, 

338 domains = None, 

339 lookups = None, 

340 sock_state_cb = None, 

341 socket_send_buffer_size = None, 

342 socket_receive_buffer_size = None, 

343 rotate = False, 

344 local_ip = None, 

345 local_dev = None, 

346 resolvconf_path = None): 

347 

348 channel = _ffi.new("ares_channel *") 

349 options = _ffi.new("struct ares_options *") 

350 optmask = 0 

351 

352 if flags is not None: 

353 options.flags = flags 

354 optmask = optmask | _lib.ARES_OPT_FLAGS 

355 

356 if timeout is not None: 

357 options.timeout = int(timeout * 1000) 

358 optmask = optmask | _lib.ARES_OPT_TIMEOUTMS 

359 

360 if tries is not None: 

361 options.tries = tries 

362 optmask = optmask | _lib.ARES_OPT_TRIES 

363 

364 if ndots is not None: 

365 options.ndots = ndots 

366 optmask = optmask | _lib.ARES_OPT_NDOTS 

367 

368 if tcp_port is not None: 

369 options.tcp_port = tcp_port 

370 optmask = optmask | _lib.ARES_OPT_TCP_PORT 

371 

372 if udp_port is not None: 

373 options.udp_port = udp_port 

374 optmask = optmask | _lib.ARES_OPT_UDP_PORT 

375 

376 if socket_send_buffer_size is not None: 

377 options.socket_send_buffer_size = socket_send_buffer_size 

378 optmask = optmask | _lib.ARES_OPT_SOCK_SNDBUF 

379 

380 if socket_receive_buffer_size is not None: 

381 options.socket_receive_buffer_size = socket_receive_buffer_size 

382 optmask = optmask | _lib.ARES_OPT_SOCK_RCVBUF 

383 

384 if sock_state_cb: 

385 if not callable(sock_state_cb): 

386 raise TypeError("sock_state_cb is not callable") 

387 

388 userdata = _ffi.new_handle(sock_state_cb) 

389 

390 # This must be kept alive while the channel is alive. 

391 self._sock_state_cb_handle = userdata 

392 

393 options.sock_state_cb = _lib._sock_state_cb 

394 options.sock_state_cb_data = userdata 

395 optmask = optmask | _lib.ARES_OPT_SOCK_STATE_CB 

396 

397 if lookups: 

398 options.lookups = _ffi.new('char[]', ascii_bytes(lookups)) 

399 optmask = optmask | _lib.ARES_OPT_LOOKUPS 

400 

401 if domains: 

402 strs = [_ffi.new("char[]", ascii_bytes(i)) for i in domains] 

403 c = _ffi.new("char *[%d]" % (len(domains) + 1)) 

404 for i in range(len(domains)): 

405 c[i] = strs[i] 

406 

407 options.domains = c 

408 options.ndomains = len(domains) 

409 optmask = optmask | _lib.ARES_OPT_DOMAINS 

410 

411 if rotate: 

412 optmask = optmask | _lib.ARES_OPT_ROTATE 

413 

414 if resolvconf_path is not None: 

415 optmask = optmask | _lib.ARES_OPT_RESOLVCONF 

416 options.resolvconf_path = _ffi.new('char[]', ascii_bytes(resolvconf_path)) 

417 

418 r = _lib.ares_init_options(channel, options, optmask) 

419 if r != _lib.ARES_SUCCESS: 

420 raise AresError('Failed to initialize c-ares channel') 

421 

422 self._channel = _ffi.gc(channel, lambda x: _lib.ares_destroy(x[0])) 

423 

424 if servers: 

425 self.servers = servers 

426 

427 if local_ip: 

428 self.set_local_ip(local_ip) 

429 

430 if local_dev: 

431 self.set_local_dev(local_dev) 

432 

433 def cancel(self): 

434 _lib.ares_cancel(self._channel[0]) 

435 

436 @property 

437 def servers(self): 

438 servers = _ffi.new("struct ares_addr_node **") 

439 

440 r = _lib.ares_get_servers(self._channel[0], servers) 

441 if r != _lib.ARES_SUCCESS: 

442 raise AresError(r, errno.strerror(r)) 

443 

444 server_list = [] 

445 server = _ffi.new("struct ares_addr_node **", servers[0]) 

446 while True: 

447 if server == _ffi.NULL: 

448 break 

449 

450 ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN) 

451 s = server[0] 

452 if _ffi.NULL != _lib.ares_inet_ntop(s.family, _ffi.addressof(s.addr), ip, _lib.INET6_ADDRSTRLEN): 

453 server_list.append(maybe_str(_ffi.string(ip, _lib.INET6_ADDRSTRLEN))) 

454 

455 server = s.next 

456 

457 return server_list 

458 

459 @servers.setter 

460 def servers(self, servers): 

461 c = _ffi.new("struct ares_addr_node[%d]" % len(servers)) 

462 for i, server in enumerate(servers): 

463 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(server), _ffi.addressof(c[i].addr.addr4)) == 1: 

464 c[i].family = socket.AF_INET 

465 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(server), _ffi.addressof(c[i].addr.addr6)) == 1: 

466 c[i].family = socket.AF_INET6 

467 else: 

468 raise ValueError("invalid IP address") 

469 

470 if i > 0: 

471 c[i - 1].next = _ffi.addressof(c[i]) 

472 

473 r = _lib.ares_set_servers(self._channel[0], c) 

474 if r != _lib.ARES_SUCCESS: 

475 raise AresError(r, errno.strerror(r)) 

476 

477 def getsock(self): 

478 rfds = [] 

479 wfds = [] 

480 socks = _ffi.new("ares_socket_t [%d]" % _lib.ARES_GETSOCK_MAXNUM) 

481 bitmask = _lib.ares_getsock(self._channel[0], socks, _lib.ARES_GETSOCK_MAXNUM) 

482 for i in range(_lib.ARES_GETSOCK_MAXNUM): 

483 if _lib.ARES_GETSOCK_READABLE(bitmask, i): 

484 rfds.append(socks[i]) 

485 if _lib.ARES_GETSOCK_WRITABLE(bitmask, i): 

486 wfds.append(socks[i]) 

487 

488 return rfds, wfds 

489 

490 def process_fd(self, read_fd, write_fd): 

491 _lib.ares_process_fd(self._channel[0], _ffi.cast("ares_socket_t", read_fd), _ffi.cast("ares_socket_t", write_fd)) 

492 

493 def timeout(self, t = None): 

494 maxtv = _ffi.NULL 

495 tv = _ffi.new("struct timeval*") 

496 

497 if t is not None: 

498 if t >= 0.0: 

499 maxtv = _ffi.new("struct timeval*") 

500 maxtv.tv_sec = int(math.floor(t)) 

501 maxtv.tv_usec = int(math.fmod(t, 1.0) * 1000000) 

502 else: 

503 raise ValueError("timeout needs to be a positive number or None") 

504 

505 _lib.ares_timeout(self._channel[0], maxtv, tv) 

506 

507 if tv == _ffi.NULL: 

508 return 0.0 

509 

510 return (tv.tv_sec + tv.tv_usec / 1000000.0) 

511 

512 def gethostbyaddr(self, addr, callback): 

513 if not callable(callback): 

514 raise TypeError("a callable is required") 

515 

516 addr4 = _ffi.new("struct in_addr*") 

517 addr6 = _ffi.new("struct ares_in6_addr*") 

518 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(addr), (addr4)) == 1: 

519 address = addr4 

520 family = socket.AF_INET 

521 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(addr), (addr6)) == 1: 

522 address = addr6 

523 family = socket.AF_INET6 

524 else: 

525 raise ValueError("invalid IP address") 

526 

527 userdata = _ffi.new_handle(callback) 

528 _global_set.add(userdata) 

529 _lib.ares_gethostbyaddr(self._channel[0], address, _ffi.sizeof(address[0]), family, _lib._host_cb, userdata) 

530 

531 def gethostbyname(self, name, family, callback): 

532 if not callable(callback): 

533 raise TypeError("a callable is required") 

534 

535 userdata = _ffi.new_handle(callback) 

536 _global_set.add(userdata) 

537 _lib.ares_gethostbyname(self._channel[0], parse_name(name), family, _lib._host_cb, userdata) 

538 

539 def getaddrinfo(self, host, port, callback, family=0, type=0, proto=0, flags=0): 

540 if not callable(callback): 

541 raise TypeError("a callable is required") 

542 

543 if port is None: 

544 service = _ffi.NULL 

545 elif isinstance(port, int): 

546 service = str(port).encode('ascii') 

547 else: 

548 service = ascii_bytes(port) 

549 

550 userdata = _ffi.new_handle(callback) 

551 _global_set.add(userdata) 

552 

553 hints = _ffi.new('struct ares_addrinfo_hints*') 

554 hints.ai_flags = flags 

555 hints.ai_family = family 

556 hints.ai_socktype = type 

557 hints.ai_protocol = proto 

558 _lib.ares_getaddrinfo(self._channel[0], parse_name(host), service, hints, _lib._addrinfo_cb, userdata) 

559 

560 def query(self, name, query_type, callback, query_class=None): 

561 self._do_query(_lib.ares_query, name, query_type, callback, query_class=query_class) 

562 

563 def search(self, name, query_type, callback, query_class=None): 

564 self._do_query(_lib.ares_search, name, query_type, callback, query_class=query_class) 

565 

566 def _do_query(self, func, name, query_type, callback, query_class=None): 

567 if not callable(callback): 

568 raise TypeError('a callable is required') 

569 

570 if query_type not in self.__qtypes__: 

571 raise ValueError('invalid query type specified') 

572 

573 if query_class is None: 

574 query_class = _lib.C_IN 

575 

576 if query_class not in self.__qclasses__: 

577 raise ValueError('invalid query class specified') 

578 

579 userdata = _ffi.new_handle((callback, query_type)) 

580 _global_set.add(userdata) 

581 func(self._channel[0], parse_name(name), query_class, query_type, _lib._query_cb, userdata) 

582 

583 def set_local_ip(self, ip): 

584 addr4 = _ffi.new("struct in_addr*") 

585 addr6 = _ffi.new("struct ares_in6_addr*") 

586 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), addr4) == 1: 

587 _lib.ares_set_local_ip4(self._channel[0], socket.ntohl(addr4.s_addr)) 

588 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), addr6) == 1: 

589 _lib.ares_set_local_ip6(self._channel[0], addr6) 

590 else: 

591 raise ValueError("invalid IP address") 

592 

593 def getnameinfo(self, address, flags, callback): 

594 if not callable(callback): 

595 raise TypeError("a callable is required") 

596 

597 if len(address) == 2: 

598 (ip, port) = address 

599 sa4 = _ffi.new("struct sockaddr_in*") 

600 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), _ffi.addressof(sa4.sin_addr)) != 1: 

601 raise ValueError("Invalid IPv4 address %r" % ip) 

602 sa4.sin_family = socket.AF_INET 

603 sa4.sin_port = socket.htons(port) 

604 sa = sa4 

605 elif len(address) == 4: 

606 (ip, port, flowinfo, scope_id) = address 

607 sa6 = _ffi.new("struct sockaddr_in6*") 

608 if _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), _ffi.addressof(sa6.sin6_addr)) != 1: 

609 raise ValueError("Invalid IPv6 address %r" % ip) 

610 sa6.sin6_family = socket.AF_INET6 

611 sa6.sin6_port = socket.htons(port) 

612 sa6.sin6_flowinfo = socket.htonl(flowinfo) # I'm unsure about byteorder here. 

613 sa6.sin6_scope_id = scope_id # Yes, without htonl. 

614 sa = sa6 

615 else: 

616 raise ValueError("Invalid address argument") 

617 

618 userdata = _ffi.new_handle(callback) 

619 _global_set.add(userdata) 

620 _lib.ares_getnameinfo(self._channel[0], _ffi.cast("struct sockaddr*", sa), _ffi.sizeof(sa[0]), flags, _lib._nameinfo_cb, userdata) 

621 

622 def set_local_dev(self, dev): 

623 _lib.ares_set_local_dev(self._channel[0], dev) 

624 

625 

626class AresResult: 

627 __slots__ = () 

628 

629 def __repr__(self): 

630 attrs = ['%s=%s' % (a, getattr(self, a)) for a in self.__slots__] 

631 return '<%s> %s' % (self.__class__.__name__, ', '.join(attrs)) 

632 

633 

634# DNS query result types 

635# 

636 

637class ares_query_a_result(AresResult): 

638 __slots__ = ('host', 'ttl') 

639 type = 'A' 

640 

641 def __init__(self, ares_addrttl): 

642 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN) 

643 _lib.ares_inet_ntop(socket.AF_INET, _ffi.addressof(ares_addrttl.ipaddr), buf, _lib.INET6_ADDRSTRLEN) 

644 self.host = maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN)) 

645 self.ttl = ares_addrttl.ttl 

646 

647 

648class ares_query_aaaa_result(AresResult): 

649 __slots__ = ('host', 'ttl') 

650 type = 'AAAA' 

651 

652 def __init__(self, ares_addrttl): 

653 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN) 

654 _lib.ares_inet_ntop(socket.AF_INET6, _ffi.addressof(ares_addrttl.ip6addr), buf, _lib.INET6_ADDRSTRLEN) 

655 self.host = maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN)) 

656 self.ttl = ares_addrttl.ttl 

657 

658 

659class ares_query_caa_result(AresResult): 

660 __slots__ = ('critical', 'property', 'value', 'ttl') 

661 type = 'CAA' 

662 

663 def __init__(self, caa): 

664 self.critical = caa.critical 

665 self.property = maybe_str(_ffi.string(caa.property, caa.plength)) 

666 self.value = maybe_str(_ffi.string(caa.value, caa.length)) 

667 self.ttl = -1 

668 

669 

670class ares_query_cname_result(AresResult): 

671 __slots__ = ('cname', 'ttl') 

672 type = 'CNAME' 

673 

674 def __init__(self, host): 

675 self.cname = maybe_str(_ffi.string(host.h_name)) 

676 self.ttl = -1 

677 

678 

679class ares_query_mx_result(AresResult): 

680 __slots__ = ('host', 'priority', 'ttl') 

681 type = 'MX' 

682 

683 def __init__(self, mx): 

684 self.host = maybe_str(_ffi.string(mx.host)) 

685 self.priority = mx.priority 

686 self.ttl = -1 

687 

688 

689class ares_query_naptr_result(AresResult): 

690 __slots__ = ('order', 'preference', 'flags', 'service', 'regex', 'replacement', 'ttl') 

691 type = 'NAPTR' 

692 

693 def __init__(self, naptr): 

694 self.order = naptr.order 

695 self.preference = naptr.preference 

696 self.flags = maybe_str(_ffi.string(naptr.flags)) 

697 self.service = maybe_str(_ffi.string(naptr.service)) 

698 self.regex = maybe_str(_ffi.string(naptr.regexp)) 

699 self.replacement = maybe_str(_ffi.string(naptr.replacement)) 

700 self.ttl = -1 

701 

702 

703class ares_query_ns_result(AresResult): 

704 __slots__ = ('host', 'ttl') 

705 type = 'NS' 

706 

707 def __init__(self, ns): 

708 self.host = maybe_str(_ffi.string(ns)) 

709 self.ttl = -1 

710 

711 

712class ares_query_ptr_result(AresResult): 

713 __slots__ = ('name', 'ttl', 'aliases') 

714 type = 'PTR' 

715 

716 def __init__(self, hostent, aliases): 

717 self.name = maybe_str(_ffi.string(hostent.h_name)) 

718 self.aliases = aliases 

719 self.ttl = -1 

720 

721 

722class ares_query_soa_result(AresResult): 

723 __slots__ = ('nsname', 'hostmaster', 'serial', 'refresh', 'retry', 'expires', 'minttl', 'ttl') 

724 type = 'SOA' 

725 

726 def __init__(self, soa): 

727 self.nsname = maybe_str(_ffi.string(soa.nsname)) 

728 self.hostmaster = maybe_str(_ffi.string(soa.hostmaster)) 

729 self.serial = soa.serial 

730 self.refresh = soa.refresh 

731 self.retry = soa.retry 

732 self.expires = soa.expire 

733 self.minttl = soa.minttl 

734 self.ttl = -1 

735 

736 

737class ares_query_srv_result(AresResult): 

738 __slots__ = ('host', 'port', 'priority', 'weight', 'ttl') 

739 type = 'SRV' 

740 

741 def __init__(self, srv): 

742 self.host = maybe_str(_ffi.string(srv.host)) 

743 self.port = srv.port 

744 self.priority = srv.priority 

745 self.weight = srv.weight 

746 self.ttl = -1 

747 

748 

749class ares_query_txt_result(AresResult): 

750 __slots__ = ('text', 'ttl') 

751 type = 'TXT' 

752 

753 def __init__(self, txt_chunk): 

754 self.text = maybe_str(txt_chunk.text) 

755 self.ttl = -1 

756 

757 

758class ares_query_txt_result_chunk(AresResult): 

759 __slots__ = ('text', 'ttl') 

760 type = 'TXT' 

761 

762 def __init__(self, txt): 

763 self.text = _ffi.string(txt.txt) 

764 self.ttl = -1 

765 

766 

767# Other result types 

768# 

769 

770class ares_host_result(AresResult): 

771 __slots__ = ('name', 'aliases', 'addresses') 

772 

773 def __init__(self, hostent): 

774 self.name = maybe_str(_ffi.string(hostent.h_name)) 

775 self.aliases = [] 

776 self.addresses = [] 

777 i = 0 

778 while hostent.h_aliases[i] != _ffi.NULL: 

779 self.aliases.append(maybe_str(_ffi.string(hostent.h_aliases[i]))) 

780 i += 1 

781 

782 i = 0 

783 while hostent.h_addr_list[i] != _ffi.NULL: 

784 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN) 

785 if _ffi.NULL != _lib.ares_inet_ntop(hostent.h_addrtype, hostent.h_addr_list[i], buf, _lib.INET6_ADDRSTRLEN): 

786 self.addresses.append(maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN))) 

787 i += 1 

788 

789 

790class ares_nameinfo_result(AresResult): 

791 __slots__ = ('node', 'service') 

792 

793 def __init__(self, node, service): 

794 self.node = maybe_str(_ffi.string(node)) 

795 self.service = maybe_str(_ffi.string(service)) if service != _ffi.NULL else None 

796 

797 

798class ares_addrinfo_node_result(AresResult): 

799 __slots__ = ('ttl', 'flags', 'family', 'socktype', 'protocol', 'addr') 

800 

801 def __init__(self, ares_node): 

802 self.ttl = ares_node.ai_ttl 

803 self.flags = ares_node.ai_flags 

804 self.socktype = ares_node.ai_socktype 

805 self.protocol = ares_node.ai_protocol 

806 

807 addr = ares_node.ai_addr 

808 assert addr.sa_family == ares_node.ai_family 

809 ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN) 

810 if addr.sa_family == socket.AF_INET: 

811 self.family = socket.AF_INET 

812 s = _ffi.cast("struct sockaddr_in*", addr) 

813 if _ffi.NULL != _lib.ares_inet_ntop(s.sin_family, _ffi.addressof(s.sin_addr), ip, _lib.INET6_ADDRSTRLEN): 

814 # (address, port) 2-tuple for AF_INET 

815 self.addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin_port)) 

816 elif addr.sa_family == socket.AF_INET6: 

817 self.family = socket.AF_INET6 

818 s = _ffi.cast("struct sockaddr_in6*", addr) 

819 if _ffi.NULL != _lib.ares_inet_ntop(s.sin6_family, _ffi.addressof(s.sin6_addr), ip, _lib.INET6_ADDRSTRLEN): 

820 # (address, port, flow info, scope id) 4-tuple for AF_INET6 

821 self.addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin6_port), s.sin6_flowinfo, s.sin6_scope_id) 

822 else: 

823 raise ValueError("invalid sockaddr family") 

824 

825 

826class ares_addrinfo_cname_result(AresResult): 

827 __slots__ = ('ttl', 'alias', 'name') 

828 

829 def __init__(self, ares_cname): 

830 self.ttl = ares_cname.ttl 

831 self.alias = maybe_str(_ffi.string(ares_cname.alias)) 

832 self.name = maybe_str(_ffi.string(ares_cname.name)) 

833 

834 

835class ares_addrinfo_result(AresResult): 

836 __slots__ = ('cnames', 'nodes') 

837 

838 def __init__(self, ares_addrinfo): 

839 self.cnames = [] 

840 self.nodes = [] 

841 cname_ptr = ares_addrinfo.cnames 

842 while cname_ptr != _ffi.NULL: 

843 self.cnames.append(ares_addrinfo_cname_result(cname_ptr)) 

844 cname_ptr = cname_ptr.next 

845 node_ptr = ares_addrinfo.nodes 

846 while node_ptr != _ffi.NULL: 

847 self.nodes.append(ares_addrinfo_node_result(node_ptr)) 

848 node_ptr = node_ptr.ai_next 

849 _lib.ares_freeaddrinfo(ares_addrinfo) 

850 

851 

852 

853__all__ = exported_pycares_symbols + list(exported_pycares_symbols_map.keys()) + ['AresError', 'Channel', 'errno', '__version__'] 

854 

855del exported_pycares_symbols, exported_pycares_symbols_map