Coverage Report

Created: 2022-02-19 20:31

/src/php-src/ext/standard/dns.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | http://www.php.net/license/3_01.txt                                  |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: The typical suspects                                        |
14
   |          Pollita <pollita@php.net>                                   |
15
   |          Marcus Boerger <helly@php.net>                              |
16
   +----------------------------------------------------------------------+
17
 */
18
19
/* {{{ includes */
20
#include "php.h"
21
#include "php_network.h"
22
23
#if HAVE_SYS_SOCKET_H
24
#include <sys/socket.h>
25
#endif
26
27
#ifdef PHP_WIN32
28
# include "win32/inet.h"
29
# include <winsock2.h>
30
# include <windows.h>
31
# include <Ws2tcpip.h>
32
#else
33
#include <netinet/in.h>
34
#if HAVE_ARPA_INET_H
35
#include <arpa/inet.h>
36
#endif
37
#include <netdb.h>
38
#ifdef _OSD_POSIX
39
#undef STATUS
40
#undef T_UNSPEC
41
#endif
42
#if HAVE_ARPA_NAMESER_H
43
#ifdef DARWIN
44
# define BIND_8_COMPAT 1
45
#endif
46
#include <arpa/nameser.h>
47
#endif
48
#if HAVE_RESOLV_H
49
#include <resolv.h>
50
#endif
51
#ifdef HAVE_DNS_H
52
#include <dns.h>
53
#endif
54
#endif
55
56
#ifndef MAXHOSTNAMELEN
57
#define MAXHOSTNAMELEN 255
58
#endif
59
60
/* For the local hostname obtained via gethostname which is different from the
61
   dns-related MAXHOSTNAMELEN constant above */
62
#ifndef HOST_NAME_MAX
63
#define HOST_NAME_MAX 255
64
#endif
65
66
#include "php_dns.h"
67
68
/* type compat */
69
#ifndef DNS_T_A
70
0
#define DNS_T_A   1
71
#endif
72
#ifndef DNS_T_NS
73
0
#define DNS_T_NS  2
74
#endif
75
#ifndef DNS_T_CNAME
76
0
#define DNS_T_CNAME 5
77
#endif
78
#ifndef DNS_T_SOA
79
0
#define DNS_T_SOA 6
80
#endif
81
#ifndef DNS_T_PTR
82
0
#define DNS_T_PTR 12
83
#endif
84
#ifndef DNS_T_HINFO
85
0
#define DNS_T_HINFO 13
86
#endif
87
#ifndef DNS_T_MINFO
88
#define DNS_T_MINFO 14
89
#endif
90
#ifndef DNS_T_MX
91
0
#define DNS_T_MX  15
92
#endif
93
#ifndef DNS_T_TXT
94
0
#define DNS_T_TXT 16
95
#endif
96
#ifndef DNS_T_AAAA
97
0
#define DNS_T_AAAA  28
98
#endif
99
#ifndef DNS_T_SRV
100
0
#define DNS_T_SRV 33
101
#endif
102
#ifndef DNS_T_NAPTR
103
0
#define DNS_T_NAPTR 35
104
#endif
105
#ifndef DNS_T_A6
106
0
#define DNS_T_A6  38
107
#endif
108
#ifndef DNS_T_CAA
109
0
#define DNS_T_CAA 257
110
#endif
111
112
#ifndef DNS_T_ANY
113
0
#define DNS_T_ANY 255
114
#endif
115
/* }}} */
116
117
static zend_string *php_gethostbyaddr(char *ip);
118
static zend_string *php_gethostbyname(char *name);
119
120
#ifdef HAVE_GETHOSTNAME
121
/* {{{ Get the host name of the current machine */
122
PHP_FUNCTION(gethostname)
123
0
{
124
0
  char buf[HOST_NAME_MAX + 1];
125
126
0
  ZEND_PARSE_PARAMETERS_NONE();
127
128
0
  if (gethostname(buf, sizeof(buf))) {
129
0
    php_error_docref(NULL, E_WARNING, "Unable to fetch host [%d]: %s", errno, strerror(errno));
130
0
    RETURN_FALSE;
131
0
  }
132
133
0
  RETURN_STRING(buf);
134
0
}
135
/* }}} */
136
#endif
137
138
/* TODO: Reimplement the gethostby* functions using the new winxp+ API, in dns_win32.c, then
139
 we can have a dns.c, dns_unix.c and dns_win32.c instead of a messy dns.c full of #ifdef
140
*/
141
142
/* {{{ Get the Internet host name corresponding to a given IP address */
143
PHP_FUNCTION(gethostbyaddr)
144
0
{
145
0
  char *addr;
146
0
  size_t addr_len;
147
0
  zend_string *hostname;
148
149
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
150
0
    Z_PARAM_STRING(addr, addr_len)
151
0
  ZEND_PARSE_PARAMETERS_END();
152
153
0
  hostname = php_gethostbyaddr(addr);
154
155
0
  if (hostname == NULL) {
156
0
#if HAVE_IPV6 && HAVE_INET_PTON
157
0
    php_error_docref(NULL, E_WARNING, "Address is not a valid IPv4 or IPv6 address");
158
#else
159
    php_error_docref(NULL, E_WARNING, "Address is not in a.b.c.d form");
160
#endif
161
0
    RETVAL_FALSE;
162
0
  } else {
163
0
    RETVAL_STR(hostname);
164
0
  }
165
0
}
166
/* }}} */
167
168
/* {{{ php_gethostbyaddr */
169
static zend_string *php_gethostbyaddr(char *ip)
170
0
{
171
0
#if HAVE_IPV6 && HAVE_INET_PTON
172
0
  struct in6_addr addr6;
173
0
#endif
174
0
  struct in_addr addr;
175
0
  struct hostent *hp;
176
177
0
#if HAVE_IPV6 && HAVE_INET_PTON
178
0
  if (inet_pton(AF_INET6, ip, &addr6)) {
179
0
    hp = gethostbyaddr((char *) &addr6, sizeof(addr6), AF_INET6);
180
0
  } else if (inet_pton(AF_INET, ip, &addr)) {
181
0
    hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
182
0
  } else {
183
0
    return NULL;
184
0
  }
185
#else
186
  addr.s_addr = inet_addr(ip);
187
188
  if (addr.s_addr == -1) {
189
    return NULL;
190
  }
191
192
  hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
193
#endif
194
195
0
  if (!hp || hp->h_name == NULL || hp->h_name[0] == '\0') {
196
0
    return zend_string_init(ip, strlen(ip), 0);
197
0
  }
198
199
0
  return zend_string_init(hp->h_name, strlen(hp->h_name), 0);
200
0
}
201
/* }}} */
202
203
/* {{{ Get the IP address corresponding to a given Internet host name */
204
PHP_FUNCTION(gethostbyname)
205
0
{
206
0
  char *hostname;
207
0
  size_t hostname_len;
208
209
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
210
0
    Z_PARAM_STRING(hostname, hostname_len)
211
0
  ZEND_PARSE_PARAMETERS_END();
212
213
0
  if(hostname_len > MAXFQDNLEN) {
214
    /* name too long, protect from CVE-2015-0235 */
215
0
    php_error_docref(NULL, E_WARNING, "Host name is too long, the limit is %d characters", MAXFQDNLEN);
216
0
    RETURN_STRINGL(hostname, hostname_len);
217
0
  }
218
219
0
  RETURN_STR(php_gethostbyname(hostname));
220
0
}
221
/* }}} */
222
223
/* {{{ Return a list of IP addresses that a given hostname resolves to. */
224
PHP_FUNCTION(gethostbynamel)
225
0
{
226
0
  char *hostname;
227
0
  size_t hostname_len;
228
0
  struct hostent *hp;
229
0
  struct in_addr in;
230
0
  int i;
231
232
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
233
0
    Z_PARAM_STRING(hostname, hostname_len)
234
0
  ZEND_PARSE_PARAMETERS_END();
235
236
0
  if(hostname_len > MAXFQDNLEN) {
237
    /* name too long, protect from CVE-2015-0235 */
238
0
    php_error_docref(NULL, E_WARNING, "Host name is too long, the limit is %d characters", MAXFQDNLEN);
239
0
    RETURN_FALSE;
240
0
  }
241
242
0
  hp = php_network_gethostbyname(hostname);
243
0
  if (!hp) {
244
0
    RETURN_FALSE;
245
0
  }
246
247
0
  array_init(return_value);
248
249
0
  for (i = 0;; i++) {
250
    /* On macos h_addr_list entries may be misaligned. */
251
0
    struct in_addr *h_addr_entry; /* Don't call this h_addr, it's a macro! */
252
0
    memcpy(&h_addr_entry, &hp->h_addr_list[i], sizeof(struct in_addr *));
253
0
    if (!h_addr_entry) {
254
0
      return;
255
0
    }
256
257
0
    in = *h_addr_entry;
258
0
    add_next_index_string(return_value, inet_ntoa(in));
259
0
  }
260
0
}
261
/* }}} */
262
263
/* {{{ php_gethostbyname */
264
static zend_string *php_gethostbyname(char *name)
265
0
{
266
0
  struct hostent *hp;
267
0
  struct in_addr *h_addr_0; /* Don't call this h_addr, it's a macro! */
268
0
  struct in_addr in;
269
0
  char *address;
270
271
0
  hp = php_network_gethostbyname(name);
272
0
  if (!hp) {
273
0
    return zend_string_init(name, strlen(name), 0);
274
0
  }
275
276
  /* On macos h_addr_list entries may be misaligned. */
277
0
  memcpy(&h_addr_0, &hp->h_addr_list[0], sizeof(struct in_addr *));
278
0
  if (!h_addr_0) {
279
0
    return zend_string_init(name, strlen(name), 0);
280
0
  }
281
282
0
  memcpy(&in.s_addr, h_addr_0, sizeof(in.s_addr));
283
284
0
  address = inet_ntoa(in);
285
0
  return zend_string_init(address, strlen(address), 0);
286
0
}
287
/* }}} */
288
289
#if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32)
290
0
# define PHP_DNS_NUM_TYPES  13  /* Number of DNS Types Supported by PHP currently */
291
292
0
# define PHP_DNS_A      0x00000001
293
0
# define PHP_DNS_NS     0x00000002
294
0
# define PHP_DNS_CNAME  0x00000010
295
0
# define PHP_DNS_SOA    0x00000020
296
0
# define PHP_DNS_PTR    0x00000800
297
0
# define PHP_DNS_HINFO  0x00001000
298
0
# define PHP_DNS_CAA    0x00002000
299
0
# define PHP_DNS_MX     0x00004000
300
0
# define PHP_DNS_TXT    0x00008000
301
0
# define PHP_DNS_A6     0x01000000
302
0
# define PHP_DNS_SRV    0x02000000
303
0
# define PHP_DNS_NAPTR  0x04000000
304
0
# define PHP_DNS_AAAA   0x08000000
305
0
# define PHP_DNS_ANY    0x10000000
306
0
# define PHP_DNS_ALL    (PHP_DNS_A|PHP_DNS_NS|PHP_DNS_CNAME|PHP_DNS_SOA|PHP_DNS_PTR|PHP_DNS_HINFO|PHP_DNS_CAA|PHP_DNS_MX|PHP_DNS_TXT|PHP_DNS_A6|PHP_DNS_SRV|PHP_DNS_NAPTR|PHP_DNS_AAAA)
307
#endif /* HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) */
308
309
/* Note: These functions are defined in ext/standard/dns_win32.c for Windows! */
310
#if !defined(PHP_WIN32) && HAVE_DNS_SEARCH_FUNC
311
312
#ifndef HFIXEDSZ
313
#define HFIXEDSZ        12      /* fixed data in header <arpa/nameser.h> */
314
#endif /* HFIXEDSZ */
315
316
#ifndef QFIXEDSZ
317
#define QFIXEDSZ        4       /* fixed data in query <arpa/nameser.h> */
318
#endif /* QFIXEDSZ */
319
320
#undef MAXHOSTNAMELEN
321
#define MAXHOSTNAMELEN  1024
322
323
#ifndef MAXRESOURCERECORDS
324
#define MAXRESOURCERECORDS  64
325
#endif /* MAXRESOURCERECORDS */
326
327
typedef union {
328
  HEADER qb1;
329
  u_char qb2[65536];
330
} querybuf;
331
332
/* just a hack to free resources allocated by glibc in __res_nsend()
333
 * See also:
334
 *   res_thread_freeres() in glibc/resolv/res_init.c
335
 *   __libc_res_nsend()   in resolv/res_send.c
336
 * */
337
338
#if defined(__GLIBC__) && !defined(HAVE_DEPRECATED_DNS_FUNCS)
339
0
#define php_dns_free_res(__res__) _php_dns_free_res(__res__)
340
0
static void _php_dns_free_res(struct __res_state *res) { /* {{{ */
341
0
  int ns;
342
0
  for (ns = 0; ns < MAXNS; ns++) {
343
0
    if (res->_u._ext.nsaddrs[ns] != NULL) {
344
0
      free (res->_u._ext.nsaddrs[ns]);
345
0
      res->_u._ext.nsaddrs[ns] = NULL;
346
0
    }
347
0
  }
348
0
} /* }}} */
349
#else
350
#define php_dns_free_res(__res__)
351
#endif
352
353
/* {{{ Check DNS records corresponding to a given Internet host name or IP address */
354
PHP_FUNCTION(dns_check_record)
355
0
{
356
0
  HEADER *hp;
357
0
  querybuf answer;
358
0
  char *hostname, *rectype = NULL;
359
0
  size_t hostname_len, rectype_len = 0;
360
0
  int type = DNS_T_MX, i;
361
#if defined(HAVE_DNS_SEARCH)
362
  struct sockaddr_storage from;
363
  uint32_t fromsize = sizeof(from);
364
  dns_handle_t handle;
365
#elif defined(HAVE_RES_NSEARCH)
366
0
  struct __res_state state;
367
0
  struct __res_state *handle = &state;
368
0
#endif
369
370
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
371
0
    Z_PARAM_STRING(hostname, hostname_len)
372
0
    Z_PARAM_OPTIONAL
373
0
    Z_PARAM_STRING(rectype, rectype_len)
374
0
  ZEND_PARSE_PARAMETERS_END();
375
376
0
  if (hostname_len == 0) {
377
0
    zend_argument_value_error(1, "cannot be empty");
378
0
    RETURN_THROWS();
379
0
  }
380
381
0
  if (rectype) {
382
0
    if (!strcasecmp("A",     rectype)) type = DNS_T_A;
383
0
    else if (!strcasecmp("NS",    rectype)) type = DNS_T_NS;
384
0
    else if (!strcasecmp("MX",    rectype)) type = DNS_T_MX;
385
0
    else if (!strcasecmp("PTR",   rectype)) type = DNS_T_PTR;
386
0
    else if (!strcasecmp("ANY",   rectype)) type = DNS_T_ANY;
387
0
    else if (!strcasecmp("SOA",   rectype)) type = DNS_T_SOA;
388
0
    else if (!strcasecmp("CAA",   rectype)) type = DNS_T_CAA;
389
0
    else if (!strcasecmp("TXT",   rectype)) type = DNS_T_TXT;
390
0
    else if (!strcasecmp("CNAME", rectype)) type = DNS_T_CNAME;
391
0
    else if (!strcasecmp("AAAA",  rectype)) type = DNS_T_AAAA;
392
0
    else if (!strcasecmp("SRV",   rectype)) type = DNS_T_SRV;
393
0
    else if (!strcasecmp("NAPTR", rectype)) type = DNS_T_NAPTR;
394
0
    else if (!strcasecmp("A6",    rectype)) type = DNS_T_A6;
395
0
    else {
396
0
      php_error_docref(NULL, E_WARNING, "Type '%s' not supported", rectype);
397
0
      RETURN_FALSE;
398
0
    }
399
0
  }
400
401
#if defined(HAVE_DNS_SEARCH)
402
  handle = dns_open(NULL);
403
  if (handle == NULL) {
404
    RETURN_FALSE;
405
  }
406
#elif defined(HAVE_RES_NSEARCH)
407
0
    memset(&state, 0, sizeof(state));
408
0
    if (res_ninit(handle)) {
409
0
      RETURN_FALSE;
410
0
  }
411
#else
412
  res_init();
413
#endif
414
415
0
  i = php_dns_search(handle, hostname, C_IN, type, answer.qb2, sizeof answer);
416
0
  php_dns_free_handle(handle);
417
418
0
  if (i < 0) {
419
0
    RETURN_FALSE;
420
0
  }
421
0
  hp = (HEADER *)&answer;
422
0
  RETURN_BOOL(ntohs(hp->ancount) != 0);
423
0
}
424
/* }}} */
425
426
#if HAVE_FULL_DNS_FUNCS
427
428
0
#define CHECKCP(n) do { \
429
0
  if (cp + n > end) { \
430
0
    return NULL; \
431
0
  } \
432
0
} while (0)
433
434
/* {{{ php_parserr */
435
static u_char *php_parserr(u_char *cp, u_char *end, querybuf *answer, int type_to_fetch, int store, int raw, zval *subarray)
436
0
{
437
0
  u_short type, class, dlen;
438
0
  u_long ttl;
439
0
  long n, i;
440
0
  u_short s;
441
0
  u_char *tp, *p;
442
0
  char name[MAXHOSTNAMELEN];
443
0
  int have_v6_break = 0, in_v6_break = 0;
444
445
0
  ZVAL_UNDEF(subarray);
446
447
0
  n = dn_expand(answer->qb2, end, cp, name, sizeof(name) - 2);
448
0
  if (n < 0) {
449
0
    return NULL;
450
0
  }
451
0
  cp += n;
452
453
0
  CHECKCP(10);
454
0
  GETSHORT(type, cp);
455
0
  GETSHORT(class, cp);
456
0
  GETLONG(ttl, cp);
457
0
  GETSHORT(dlen, cp);
458
0
  CHECKCP(dlen);
459
0
    if (dlen == 0) {
460
        /* No data in the response - nothing to do */
461
0
        return NULL;
462
0
    }
463
0
    if (type_to_fetch != DNS_T_ANY && type != type_to_fetch) {
464
0
    cp += dlen;
465
0
    return cp;
466
0
  }
467
468
0
  if (!store) {
469
0
    cp += dlen;
470
0
    return cp;
471
0
  }
472
473
0
  array_init(subarray);
474
475
0
  add_assoc_string(subarray, "host", name);
476
0
  add_assoc_string(subarray, "class", "IN");
477
0
  add_assoc_long(subarray, "ttl", ttl);
478
0
  (void) class;
479
480
0
  if (raw) {
481
0
    add_assoc_long(subarray, "type", type);
482
0
    add_assoc_stringl(subarray, "data", (char*) cp, (uint32_t) dlen);
483
0
    cp += dlen;
484
0
    return cp;
485
0
  }
486
487
0
  switch (type) {
488
0
    case DNS_T_A:
489
0
      CHECKCP(4);
490
0
      add_assoc_string(subarray, "type", "A");
491
0
      snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
492
0
      add_assoc_string(subarray, "ip", name);
493
0
      cp += dlen;
494
0
      break;
495
0
    case DNS_T_MX:
496
0
      CHECKCP(2);
497
0
      add_assoc_string(subarray, "type", "MX");
498
0
      GETSHORT(n, cp);
499
0
      add_assoc_long(subarray, "pri", n);
500
      /* no break; */
501
0
    case DNS_T_CNAME:
502
0
      if (type == DNS_T_CNAME) {
503
0
        add_assoc_string(subarray, "type", "CNAME");
504
0
      }
505
      /* no break; */
506
0
    case DNS_T_NS:
507
0
      if (type == DNS_T_NS) {
508
0
        add_assoc_string(subarray, "type", "NS");
509
0
      }
510
      /* no break; */
511
0
    case DNS_T_PTR:
512
0
      if (type == DNS_T_PTR) {
513
0
        add_assoc_string(subarray, "type", "PTR");
514
0
      }
515
0
      n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
516
0
      if (n < 0) {
517
0
        return NULL;
518
0
      }
519
0
      cp += n;
520
0
      add_assoc_string(subarray, "target", name);
521
0
      break;
522
0
    case DNS_T_HINFO:
523
      /* See RFC 1010 for values */
524
0
      add_assoc_string(subarray, "type", "HINFO");
525
0
      CHECKCP(1);
526
0
      n = *cp & 0xFF;
527
0
      cp++;
528
0
      CHECKCP(n);
529
0
      add_assoc_stringl(subarray, "cpu", (char*)cp, n);
530
0
      cp += n;
531
0
      CHECKCP(1);
532
0
      n = *cp & 0xFF;
533
0
      cp++;
534
0
      CHECKCP(n);
535
0
      add_assoc_stringl(subarray, "os", (char*)cp, n);
536
0
      cp += n;
537
0
      break;
538
0
    case DNS_T_CAA:
539
      /* See RFC 6844 for values https://tools.ietf.org/html/rfc6844 */
540
0
      add_assoc_string(subarray, "type", "CAA");
541
      // 1 flag byte
542
0
      CHECKCP(1);
543
0
      n = *cp & 0xFF;
544
0
      add_assoc_long(subarray, "flags", n);
545
0
      cp++;
546
      // Tag length (1 byte)
547
0
      CHECKCP(1);
548
0
      n = *cp & 0xFF;
549
0
      cp++;
550
0
      CHECKCP(n);
551
0
      add_assoc_stringl(subarray, "tag", (char*)cp, n);
552
0
      cp += n;
553
0
      if ( (size_t) dlen < ((size_t)n) + 2 ) {
554
0
        return NULL;
555
0
      }
556
0
      n = dlen - n - 2;
557
0
      CHECKCP(n);
558
0
      add_assoc_stringl(subarray, "value", (char*)cp, n);
559
0
      cp += n;
560
0
      break;
561
0
    case DNS_T_TXT:
562
0
      {
563
0
        int l1 = 0, l2 = 0;
564
0
        zval entries;
565
0
        zend_string *tp;
566
567
0
        add_assoc_string(subarray, "type", "TXT");
568
0
        tp = zend_string_alloc(dlen, 0);
569
570
0
        array_init(&entries);
571
572
0
        while (l1 < dlen) {
573
0
          n = cp[l1];
574
0
          if ((l1 + n) >= dlen) {
575
            // Invalid chunk length, truncate
576
0
            n = dlen - (l1 + 1);
577
0
          }
578
0
          if (n) {
579
0
            memcpy(ZSTR_VAL(tp) + l2 , cp + l1 + 1, n);
580
0
            add_next_index_stringl(&entries, (char *) cp + l1 + 1, n);
581
0
          }
582
0
          l1 = l1 + n + 1;
583
0
          l2 = l2 + n;
584
0
        }
585
0
        ZSTR_VAL(tp)[l2] = '\0';
586
0
        ZSTR_LEN(tp) = l2;
587
0
        cp += dlen;
588
589
0
        add_assoc_str(subarray, "txt", tp);
590
0
        add_assoc_zval(subarray, "entries", &entries);
591
0
      }
592
0
      break;
593
0
    case DNS_T_SOA:
594
0
      add_assoc_string(subarray, "type", "SOA");
595
0
      n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
596
0
      if (n < 0) {
597
0
        return NULL;
598
0
      }
599
0
      cp += n;
600
0
      add_assoc_string(subarray, "mname", name);
601
0
      n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
602
0
      if (n < 0) {
603
0
        return NULL;
604
0
      }
605
0
      cp += n;
606
0
      add_assoc_string(subarray, "rname", name);
607
0
      CHECKCP(5*4);
608
0
      GETLONG(n, cp);
609
0
      add_assoc_long(subarray, "serial", n);
610
0
      GETLONG(n, cp);
611
0
      add_assoc_long(subarray, "refresh", n);
612
0
      GETLONG(n, cp);
613
0
      add_assoc_long(subarray, "retry", n);
614
0
      GETLONG(n, cp);
615
0
      add_assoc_long(subarray, "expire", n);
616
0
      GETLONG(n, cp);
617
0
      add_assoc_long(subarray, "minimum-ttl", n);
618
0
      break;
619
0
    case DNS_T_AAAA:
620
0
      tp = (u_char*)name;
621
0
      CHECKCP(8*2);
622
0
      for(i=0; i < 8; i++) {
623
0
        GETSHORT(s, cp);
624
0
        if (s != 0) {
625
0
          if (tp > (u_char *)name) {
626
0
            in_v6_break = 0;
627
0
            tp[0] = ':';
628
0
            tp++;
629
0
          }
630
0
          tp += sprintf((char*)tp,"%x",s);
631
0
        } else {
632
0
          if (!have_v6_break) {
633
0
            have_v6_break = 1;
634
0
            in_v6_break = 1;
635
0
            tp[0] = ':';
636
0
            tp++;
637
0
          } else if (!in_v6_break) {
638
0
            tp[0] = ':';
639
0
            tp++;
640
0
            tp[0] = '0';
641
0
            tp++;
642
0
          }
643
0
        }
644
0
      }
645
0
      if (have_v6_break && in_v6_break) {
646
0
        tp[0] = ':';
647
0
        tp++;
648
0
      }
649
0
      tp[0] = '\0';
650
0
      add_assoc_string(subarray, "type", "AAAA");
651
0
      add_assoc_string(subarray, "ipv6", name);
652
0
      break;
653
0
    case DNS_T_A6:
654
0
      p = cp;
655
0
      add_assoc_string(subarray, "type", "A6");
656
0
      CHECKCP(1);
657
0
      n = ((int)cp[0]) & 0xFF;
658
0
      cp++;
659
0
      add_assoc_long(subarray, "masklen", n);
660
0
      tp = (u_char*)name;
661
0
      if (n > 15) {
662
0
        have_v6_break = 1;
663
0
        in_v6_break = 1;
664
0
        tp[0] = ':';
665
0
        tp++;
666
0
      }
667
0
      if (n % 16 > 8) {
668
        /* Partial short */
669
0
        if (cp[0] != 0) {
670
0
          if (tp > (u_char *)name) {
671
0
            in_v6_break = 0;
672
0
            tp[0] = ':';
673
0
            tp++;
674
0
          }
675
0
          sprintf((char*)tp, "%x", cp[0] & 0xFF);
676
0
        } else {
677
0
          if (!have_v6_break) {
678
0
            have_v6_break = 1;
679
0
            in_v6_break = 1;
680
0
            tp[0] = ':';
681
0
            tp++;
682
0
          } else if (!in_v6_break) {
683
0
            tp[0] = ':';
684
0
            tp++;
685
0
            tp[0] = '0';
686
0
            tp++;
687
0
          }
688
0
        }
689
0
        cp++;
690
0
      }
691
0
      for (i = (n + 8) / 16; i < 8; i++) {
692
0
        CHECKCP(2);
693
0
        GETSHORT(s, cp);
694
0
        if (s != 0) {
695
0
          if (tp > (u_char *)name) {
696
0
            in_v6_break = 0;
697
0
            tp[0] = ':';
698
0
            tp++;
699
0
          }
700
0
          tp += sprintf((char*)tp,"%x",s);
701
0
        } else {
702
0
          if (!have_v6_break) {
703
0
            have_v6_break = 1;
704
0
            in_v6_break = 1;
705
0
            tp[0] = ':';
706
0
            tp++;
707
0
          } else if (!in_v6_break) {
708
0
            tp[0] = ':';
709
0
            tp++;
710
0
            tp[0] = '0';
711
0
            tp++;
712
0
          }
713
0
        }
714
0
      }
715
0
      if (have_v6_break && in_v6_break) {
716
0
        tp[0] = ':';
717
0
        tp++;
718
0
      }
719
0
      tp[0] = '\0';
720
0
      add_assoc_string(subarray, "ipv6", name);
721
0
      if (cp < p + dlen) {
722
0
        n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
723
0
        if (n < 0) {
724
0
          return NULL;
725
0
        }
726
0
        cp += n;
727
0
        add_assoc_string(subarray, "chain", name);
728
0
      }
729
0
      break;
730
0
    case DNS_T_SRV:
731
0
      CHECKCP(3*2);
732
0
      add_assoc_string(subarray, "type", "SRV");
733
0
      GETSHORT(n, cp);
734
0
      add_assoc_long(subarray, "pri", n);
735
0
      GETSHORT(n, cp);
736
0
      add_assoc_long(subarray, "weight", n);
737
0
      GETSHORT(n, cp);
738
0
      add_assoc_long(subarray, "port", n);
739
0
      n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
740
0
      if (n < 0) {
741
0
        return NULL;
742
0
      }
743
0
      cp += n;
744
0
      add_assoc_string(subarray, "target", name);
745
0
      break;
746
0
    case DNS_T_NAPTR:
747
0
      CHECKCP(2*2);
748
0
      add_assoc_string(subarray, "type", "NAPTR");
749
0
      GETSHORT(n, cp);
750
0
      add_assoc_long(subarray, "order", n);
751
0
      GETSHORT(n, cp);
752
0
      add_assoc_long(subarray, "pref", n);
753
754
0
      CHECKCP(1);
755
0
      n = (cp[0] & 0xFF);
756
0
      cp++;
757
0
      CHECKCP(n);
758
0
      add_assoc_stringl(subarray, "flags", (char*)cp, n);
759
0
      cp += n;
760
761
0
      CHECKCP(1);
762
0
      n = (cp[0] & 0xFF);
763
0
      cp++;
764
0
      CHECKCP(n);
765
0
      add_assoc_stringl(subarray, "services", (char*)cp, n);
766
0
      cp += n;
767
768
0
      CHECKCP(1);
769
0
      n = (cp[0] & 0xFF);
770
0
      cp++;
771
0
      CHECKCP(n);
772
0
      add_assoc_stringl(subarray, "regex", (char*)cp, n);
773
0
      cp += n;
774
775
0
      n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
776
0
      if (n < 0) {
777
0
        return NULL;
778
0
      }
779
0
      cp += n;
780
0
      add_assoc_string(subarray, "replacement", name);
781
0
      break;
782
0
    default:
783
0
      zval_ptr_dtor(subarray);
784
0
      ZVAL_UNDEF(subarray);
785
0
      cp += dlen;
786
0
      break;
787
0
  }
788
789
0
  return cp;
790
0
}
791
/* }}} */
792
793
/* {{{ Get any Resource Record corresponding to a given Internet host name */
794
PHP_FUNCTION(dns_get_record)
795
0
{
796
0
  char *hostname;
797
0
  size_t hostname_len;
798
0
  zend_long type_param = PHP_DNS_ANY;
799
0
  zval *authns = NULL, *addtl = NULL;
800
0
  int type_to_fetch;
801
#if defined(HAVE_DNS_SEARCH)
802
  struct sockaddr_storage from;
803
  uint32_t fromsize = sizeof(from);
804
  dns_handle_t handle;
805
#elif defined(HAVE_RES_NSEARCH)
806
0
  struct __res_state state;
807
0
  struct __res_state *handle = &state;
808
0
#endif
809
0
  HEADER *hp;
810
0
  querybuf answer;
811
0
  u_char *cp = NULL, *end = NULL;
812
0
  int n, qd, an, ns = 0, ar = 0;
813
0
  int type, first_query = 1, store_results = 1;
814
0
  zend_bool raw = 0;
815
816
0
  ZEND_PARSE_PARAMETERS_START(1, 5)
817
0
    Z_PARAM_STRING(hostname, hostname_len)
818
0
    Z_PARAM_OPTIONAL
819
0
    Z_PARAM_LONG(type_param)
820
0
    Z_PARAM_ZVAL(authns)
821
0
    Z_PARAM_ZVAL(addtl)
822
0
    Z_PARAM_BOOL(raw)
823
0
  ZEND_PARSE_PARAMETERS_END();
824
825
0
  if (authns) {
826
0
    authns = zend_try_array_init(authns);
827
0
    if (!authns) {
828
0
      RETURN_THROWS();
829
0
    }
830
0
  }
831
0
  if (addtl) {
832
0
    addtl = zend_try_array_init(addtl);
833
0
    if (!addtl) {
834
0
      RETURN_THROWS();
835
0
    }
836
0
  }
837
838
0
  if (!raw) {
839
0
    if ((type_param & ~PHP_DNS_ALL) && (type_param != PHP_DNS_ANY)) {
840
0
      php_error_docref(NULL, E_WARNING, "Type '" ZEND_LONG_FMT "' not supported", type_param);
841
0
      RETURN_FALSE;
842
0
    }
843
0
  } else {
844
0
    if ((type_param < 1) || (type_param > 0xFFFF)) {
845
0
      php_error_docref(NULL, E_WARNING,
846
0
        "Numeric DNS record type must be between 1 and 65535, '" ZEND_LONG_FMT "' given", type_param);
847
0
      RETURN_FALSE;
848
0
    }
849
0
  }
850
851
  /* Initialize the return array */
852
0
  array_init(return_value);
853
854
  /* - We emulate an or'ed type mask by querying type by type. (Steps 0 - NUMTYPES-1 )
855
   *   If additional info is wanted we check again with DNS_T_ANY (step NUMTYPES / NUMTYPES+1 )
856
   *   store_results is used to skip storing the results retrieved in step
857
   *   NUMTYPES+1 when results were already fetched.
858
   * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY. (step NUMTYPES+1 )
859
   * - In case of raw mode, we query only the requested type instead of looping type by type
860
   *   before going with the additional info stuff.
861
   */
862
863
0
  if (raw) {
864
0
    type = -1;
865
0
  } else if (type_param == PHP_DNS_ANY) {
866
0
    type = PHP_DNS_NUM_TYPES + 1;
867
0
  } else {
868
0
    type = 0;
869
0
  }
870
871
0
  for ( ;
872
0
    type < (addtl ? (PHP_DNS_NUM_TYPES + 2) : PHP_DNS_NUM_TYPES) || first_query;
873
0
    type++
874
0
  ) {
875
0
    first_query = 0;
876
0
    switch (type) {
877
0
      case -1: /* raw */
878
0
        type_to_fetch = type_param;
879
        /* skip over the rest and go directly to additional records */
880
0
        type = PHP_DNS_NUM_TYPES - 1;
881
0
        break;
882
0
      case 0:
883
0
        type_to_fetch = type_param&PHP_DNS_A     ? DNS_T_A     : 0;
884
0
        break;
885
0
      case 1:
886
0
        type_to_fetch = type_param&PHP_DNS_NS    ? DNS_T_NS    : 0;
887
0
        break;
888
0
      case 2:
889
0
        type_to_fetch = type_param&PHP_DNS_CNAME ? DNS_T_CNAME : 0;
890
0
        break;
891
0
      case 3:
892
0
        type_to_fetch = type_param&PHP_DNS_SOA   ? DNS_T_SOA   : 0;
893
0
        break;
894
0
      case 4:
895
0
        type_to_fetch = type_param&PHP_DNS_PTR   ? DNS_T_PTR   : 0;
896
0
        break;
897
0
      case 5:
898
0
        type_to_fetch = type_param&PHP_DNS_HINFO ? DNS_T_HINFO : 0;
899
0
        break;
900
0
      case 6:
901
0
        type_to_fetch = type_param&PHP_DNS_MX    ? DNS_T_MX    : 0;
902
0
        break;
903
0
      case 7:
904
0
        type_to_fetch = type_param&PHP_DNS_TXT   ? DNS_T_TXT   : 0;
905
0
        break;
906
0
      case 8:
907
0
        type_to_fetch = type_param&PHP_DNS_AAAA   ? DNS_T_AAAA  : 0;
908
0
        break;
909
0
      case 9:
910
0
        type_to_fetch = type_param&PHP_DNS_SRV   ? DNS_T_SRV   : 0;
911
0
        break;
912
0
      case 10:
913
0
        type_to_fetch = type_param&PHP_DNS_NAPTR ? DNS_T_NAPTR : 0;
914
0
        break;
915
0
      case 11:
916
0
        type_to_fetch = type_param&PHP_DNS_A6   ? DNS_T_A6 : 0;
917
0
        break;
918
0
      case 12:
919
0
        type_to_fetch = type_param&PHP_DNS_CAA ? DNS_T_CAA : 0;
920
0
        break;
921
0
      case PHP_DNS_NUM_TYPES:
922
0
        store_results = 0;
923
0
        continue;
924
0
      default:
925
0
      case (PHP_DNS_NUM_TYPES + 1):
926
0
        type_to_fetch = DNS_T_ANY;
927
0
        break;
928
0
    }
929
930
0
    if (type_to_fetch) {
931
#if defined(HAVE_DNS_SEARCH)
932
      handle = dns_open(NULL);
933
      if (handle == NULL) {
934
        zend_array_destroy(Z_ARR_P(return_value));
935
        RETURN_FALSE;
936
      }
937
#elif defined(HAVE_RES_NSEARCH)
938
0
        memset(&state, 0, sizeof(state));
939
0
        if (res_ninit(handle)) {
940
0
          zend_array_destroy(Z_ARR_P(return_value));
941
0
        RETURN_FALSE;
942
0
      }
943
#else
944
      res_init();
945
#endif
946
947
0
      n = php_dns_search(handle, hostname, C_IN, type_to_fetch, answer.qb2, sizeof answer);
948
949
0
      if (n < 0) {
950
0
        php_dns_free_handle(handle);
951
0
        switch (h_errno) {
952
0
          case NO_DATA:
953
0
          case HOST_NOT_FOUND:
954
0
            continue;
955
956
0
          case NO_RECOVERY:
957
0
            php_error_docref(NULL, E_WARNING, "An unexpected server failure occurred.");
958
0
            break;
959
960
0
          case TRY_AGAIN:
961
0
            php_error_docref(NULL, E_WARNING, "A temporary server error occurred.");
962
0
            break;
963
964
0
          default:
965
0
            php_error_docref(NULL, E_WARNING, "DNS Query failed");
966
0
        }
967
0
        zend_array_destroy(Z_ARR_P(return_value));
968
0
        RETURN_FALSE;
969
0
      }
970
971
0
      cp = answer.qb2 + HFIXEDSZ;
972
0
      end = answer.qb2 + n;
973
0
      hp = (HEADER *)&answer;
974
0
      qd = ntohs(hp->qdcount);
975
0
      an = ntohs(hp->ancount);
976
0
      ns = ntohs(hp->nscount);
977
0
      ar = ntohs(hp->arcount);
978
979
      /* Skip QD entries, they're only used by dn_expand later on */
980
0
      while (qd-- > 0) {
981
0
        n = dn_skipname(cp, end);
982
0
        if (n < 0) {
983
0
          php_error_docref(NULL, E_WARNING, "Unable to parse DNS data received");
984
0
          zend_array_destroy(Z_ARR_P(return_value));
985
0
          php_dns_free_handle(handle);
986
0
          RETURN_FALSE;
987
0
        }
988
0
        cp += n + QFIXEDSZ;
989
0
      }
990
991
      /* YAY! Our real answers! */
992
0
      while (an-- && cp && cp < end) {
993
0
        zval retval;
994
995
0
        cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval);
996
0
        if (Z_TYPE(retval) != IS_UNDEF && store_results) {
997
0
          add_next_index_zval(return_value, &retval);
998
0
        }
999
0
      }
1000
1001
0
      if (authns || addtl) {
1002
        /* List of Authoritative Name Servers
1003
         * Process when only requesting addtl so that we can skip through the section
1004
         */
1005
0
        while (ns-- > 0 && cp && cp < end) {
1006
0
          zval retval;
1007
1008
0
          cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
1009
0
          if (Z_TYPE(retval) != IS_UNDEF) {
1010
0
            add_next_index_zval(authns, &retval);
1011
0
          }
1012
0
        }
1013
0
      }
1014
1015
0
      if (addtl) {
1016
        /* Additional records associated with authoritative name servers */
1017
0
        while (ar-- > 0 && cp && cp < end) {
1018
0
          zval retval;
1019
1020
0
          cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval);
1021
0
          if (Z_TYPE(retval) != IS_UNDEF) {
1022
0
            add_next_index_zval(addtl, &retval);
1023
0
          }
1024
0
        }
1025
0
      }
1026
0
      php_dns_free_handle(handle);
1027
0
    }
1028
0
  }
1029
0
}
1030
/* }}} */
1031
1032
/* {{{ Get MX records corresponding to a given Internet host name */
1033
PHP_FUNCTION(dns_get_mx)
1034
0
{
1035
0
  char *hostname;
1036
0
  size_t hostname_len;
1037
0
  zval *mx_list, *weight_list = NULL;
1038
0
  int count, qdc;
1039
0
  u_short type, weight;
1040
0
  querybuf answer;
1041
0
  char buf[MAXHOSTNAMELEN];
1042
0
  HEADER *hp;
1043
0
  u_char *cp, *end;
1044
0
  int i;
1045
#if defined(HAVE_DNS_SEARCH)
1046
  struct sockaddr_storage from;
1047
  uint32_t fromsize = sizeof(from);
1048
  dns_handle_t handle;
1049
#elif defined(HAVE_RES_NSEARCH)
1050
0
  struct __res_state state;
1051
0
  struct __res_state *handle = &state;
1052
0
#endif
1053
1054
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
1055
0
    Z_PARAM_STRING(hostname, hostname_len)
1056
0
    Z_PARAM_ZVAL(mx_list)
1057
0
    Z_PARAM_OPTIONAL
1058
0
    Z_PARAM_ZVAL(weight_list)
1059
0
  ZEND_PARSE_PARAMETERS_END();
1060
1061
0
  mx_list = zend_try_array_init(mx_list);
1062
0
  if (!mx_list) {
1063
0
    RETURN_THROWS();
1064
0
  }
1065
1066
0
  if (weight_list) {
1067
0
    weight_list = zend_try_array_init(weight_list);
1068
0
    if (!weight_list) {
1069
0
      RETURN_THROWS();
1070
0
    }
1071
0
  }
1072
1073
#if defined(HAVE_DNS_SEARCH)
1074
  handle = dns_open(NULL);
1075
  if (handle == NULL) {
1076
    RETURN_FALSE;
1077
  }
1078
#elif defined(HAVE_RES_NSEARCH)
1079
0
    memset(&state, 0, sizeof(state));
1080
0
    if (res_ninit(handle)) {
1081
0
      RETURN_FALSE;
1082
0
  }
1083
#else
1084
  res_init();
1085
#endif
1086
1087
0
  i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, answer.qb2, sizeof answer);
1088
0
  if (i < 0) {
1089
0
    php_dns_free_handle(handle);
1090
0
    RETURN_FALSE;
1091
0
  }
1092
0
  hp = (HEADER *)&answer;
1093
0
  cp = answer.qb2 + HFIXEDSZ;
1094
0
  end = answer.qb2 + i;
1095
0
  for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) {
1096
0
    if ((i = dn_skipname(cp, end)) < 0 ) {
1097
0
      php_dns_free_handle(handle);
1098
0
      RETURN_FALSE;
1099
0
    }
1100
0
  }
1101
0
  count = ntohs((unsigned short)hp->ancount);
1102
0
  while (--count >= 0 && cp < end) {
1103
0
    if ((i = dn_skipname(cp, end)) < 0 ) {
1104
0
      php_dns_free_handle(handle);
1105
0
      RETURN_FALSE;
1106
0
    }
1107
0
    cp += i;
1108
0
    GETSHORT(type, cp);
1109
0
    cp += INT16SZ + INT32SZ;
1110
0
    GETSHORT(i, cp);
1111
0
    if (type != DNS_T_MX) {
1112
0
      cp += i;
1113
0
      continue;
1114
0
    }
1115
0
    GETSHORT(weight, cp);
1116
0
    if ((i = dn_expand(answer.qb2, end, cp, buf, sizeof(buf)-1)) < 0) {
1117
0
      php_dns_free_handle(handle);
1118
0
      RETURN_FALSE;
1119
0
    }
1120
0
    cp += i;
1121
0
    add_next_index_string(mx_list, buf);
1122
0
    if (weight_list) {
1123
0
      add_next_index_long(weight_list, weight);
1124
0
    }
1125
0
  }
1126
0
  php_dns_free_handle(handle);
1127
0
  RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL_P(mx_list)) != 0);
1128
0
}
1129
/* }}} */
1130
#endif /* HAVE_FULL_DNS_FUNCS */
1131
#endif /* !defined(PHP_WIN32) && HAVE_DNS_SEARCH_FUNC */
1132
1133
#if HAVE_FULL_DNS_FUNCS && !defined(PHP_WIN32)
1134
4.06k
PHP_MINIT_FUNCTION(dns) {
1135
4.06k
  REGISTER_LONG_CONSTANT("DNS_A",     PHP_DNS_A,     CONST_CS | CONST_PERSISTENT);
1136
4.06k
  REGISTER_LONG_CONSTANT("DNS_NS",    PHP_DNS_NS,    CONST_CS | CONST_PERSISTENT);
1137
4.06k
  REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT);
1138
4.06k
  REGISTER_LONG_CONSTANT("DNS_SOA",   PHP_DNS_SOA,   CONST_CS | CONST_PERSISTENT);
1139
4.06k
  REGISTER_LONG_CONSTANT("DNS_PTR",   PHP_DNS_PTR,   CONST_CS | CONST_PERSISTENT);
1140
4.06k
  REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT);
1141
4.06k
  REGISTER_LONG_CONSTANT("DNS_CAA",   PHP_DNS_CAA,   CONST_CS | CONST_PERSISTENT);
1142
4.06k
  REGISTER_LONG_CONSTANT("DNS_MX",    PHP_DNS_MX,    CONST_CS | CONST_PERSISTENT);
1143
4.06k
  REGISTER_LONG_CONSTANT("DNS_TXT",   PHP_DNS_TXT,   CONST_CS | CONST_PERSISTENT);
1144
4.06k
  REGISTER_LONG_CONSTANT("DNS_SRV",   PHP_DNS_SRV,   CONST_CS | CONST_PERSISTENT);
1145
4.06k
  REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT);
1146
4.06k
  REGISTER_LONG_CONSTANT("DNS_AAAA",  PHP_DNS_AAAA,  CONST_CS | CONST_PERSISTENT);
1147
4.06k
  REGISTER_LONG_CONSTANT("DNS_A6",    PHP_DNS_A6,    CONST_CS | CONST_PERSISTENT);
1148
4.06k
  REGISTER_LONG_CONSTANT("DNS_ANY",   PHP_DNS_ANY,   CONST_CS | CONST_PERSISTENT);
1149
4.06k
  REGISTER_LONG_CONSTANT("DNS_ALL",   PHP_DNS_ALL,   CONST_CS | CONST_PERSISTENT);
1150
4.06k
  return SUCCESS;
1151
4.06k
}
1152
#endif /* HAVE_FULL_DNS_FUNCS */