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