c646a30002
Many callsites of Array.set(litstr, ...)
1102 linhas
30 KiB
C++
1102 linhas
30 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/ext/ext_network.h>
|
|
#include <runtime/ext/ext_apc.h>
|
|
#include <runtime/ext/ext_string.h>
|
|
#include <runtime/base/runtime_option.h>
|
|
#include <runtime/base/server/server_stats.h>
|
|
#include <util/lock.h>
|
|
#include <runtime/base/file/file.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <arpa/nameser.h>
|
|
#include <resolv.h>
|
|
#include <util/network.h>
|
|
|
|
#if defined(__APPLE__)
|
|
# include <arpa/nameser_compat.h>
|
|
#endif
|
|
|
|
#define MAXPACKET 8192 /* max packet size used internally by BIND */
|
|
#define DNS_T_A 1
|
|
#define DNS_T_NS 2
|
|
#define DNS_T_CNAME 5
|
|
#define DNS_T_SOA 6
|
|
#define DNS_T_PTR 12
|
|
#define DNS_T_HINFO 13
|
|
#define DNS_T_MINFO 14
|
|
#define DNS_T_MX 15
|
|
#define DNS_T_TXT 16
|
|
#define DNS_T_AAAA 28
|
|
#define DNS_T_SRV 33
|
|
#define DNS_T_NAPTR 35
|
|
#define DNS_T_A6 38
|
|
#define DNS_T_ANY 255
|
|
|
|
#define PHP_DNS_NUM_TYPES 12 // Number of DNS Types Supported by PHP
|
|
#define PHP_DNS_A 0x00000001
|
|
#define PHP_DNS_NS 0x00000002
|
|
#define PHP_DNS_CNAME 0x00000010
|
|
#define PHP_DNS_SOA 0x00000020
|
|
#define PHP_DNS_PTR 0x00000800
|
|
#define PHP_DNS_HINFO 0x00001000
|
|
#define PHP_DNS_MX 0x00004000
|
|
#define PHP_DNS_TXT 0x00008000
|
|
#define PHP_DNS_A6 0x01000000
|
|
#define PHP_DNS_SRV 0x02000000
|
|
#define PHP_DNS_NAPTR 0x04000000
|
|
#define PHP_DNS_AAAA 0x08000000
|
|
#define PHP_DNS_ANY 0x10000000
|
|
#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)
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DNS
|
|
|
|
static Mutex NetworkMutex;
|
|
|
|
class ResolverInit {
|
|
public:
|
|
ResolverInit() : m_res(NULL) {
|
|
m_res = (struct __res_state *)calloc(1, sizeof(*m_res));
|
|
if (res_ninit(m_res)) {
|
|
free(m_res);
|
|
m_res = NULL;
|
|
}
|
|
}
|
|
~ResolverInit() {
|
|
if (m_res)
|
|
free(m_res);
|
|
m_res = NULL;
|
|
}
|
|
|
|
struct __res_state *getResolver(void) {
|
|
return m_res;
|
|
}
|
|
|
|
static DECLARE_THREAD_LOCAL(ResolverInit, s_res);
|
|
private:
|
|
struct __res_state *m_res;
|
|
};
|
|
IMPLEMENT_THREAD_LOCAL(ResolverInit, ResolverInit::s_res);
|
|
|
|
Variant f_gethostname() {
|
|
struct addrinfo hints, *res;
|
|
char h_name[NI_MAXHOST];
|
|
int error;
|
|
String canon_hname;
|
|
|
|
error = gethostname(h_name, NI_MAXHOST);
|
|
if (error) {
|
|
return false;
|
|
}
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_flags = AI_CANONNAME;
|
|
|
|
error = getaddrinfo(h_name, NULL, &hints, &res);
|
|
if (error) {
|
|
return String(h_name, CopyString);
|
|
}
|
|
|
|
canon_hname = String(res->ai_canonname, CopyString);
|
|
freeaddrinfo(res);
|
|
return canon_hname;
|
|
}
|
|
|
|
Variant f_gethostbyaddr(CStrRef ip_address) {
|
|
IOStatusHelper io("gethostbyaddr", ip_address.data());
|
|
struct addrinfo hints, *res, *res0;
|
|
char h_name[NI_MAXHOST];
|
|
int error;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = PF_UNSPEC;
|
|
error = getaddrinfo(ip_address.data(), NULL, &hints, &res0);
|
|
if (error) {
|
|
return false;
|
|
}
|
|
|
|
for (res = res0; res; res = res->ai_next) {
|
|
if (getnameinfo(res->ai_addr, res->ai_addrlen, h_name, NI_MAXHOST,
|
|
NULL, 0, 0) < 0) {
|
|
continue;
|
|
}
|
|
freeaddrinfo(res0);
|
|
return String(h_name, CopyString);
|
|
}
|
|
freeaddrinfo(res0);
|
|
return ip_address;
|
|
}
|
|
|
|
String f_gethostbyname(CStrRef hostname) {
|
|
IOStatusHelper io("gethostbyname", hostname.data());
|
|
if (RuntimeOption::EnableDnsCache) {
|
|
Variant success;
|
|
Variant resolved = f_apc_fetch(hostname, ref(success),
|
|
SHARED_STORE_DNS_CACHE);
|
|
if (same(success, true)) {
|
|
if (same(resolved, false)) {
|
|
return hostname;
|
|
}
|
|
return resolved.toString();
|
|
}
|
|
}
|
|
|
|
Util::HostEnt result;
|
|
if (!Util::safe_gethostbyname(hostname.data(), result)) {
|
|
if (RuntimeOption::EnableDnsCache) {
|
|
f_apc_store(hostname, false, RuntimeOption::DnsCacheTTL,
|
|
SHARED_STORE_DNS_CACHE);
|
|
}
|
|
return hostname;
|
|
}
|
|
|
|
struct in_addr in;
|
|
memcpy(&in.s_addr, *(result.hostbuf.h_addr_list), sizeof(in.s_addr));
|
|
String ret(Util::safe_inet_ntoa(in));
|
|
if (RuntimeOption::EnableDnsCache) {
|
|
f_apc_store(hostname, ret, RuntimeOption::DnsCacheTTL,
|
|
SHARED_STORE_DNS_CACHE);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant f_gethostbynamel(CStrRef hostname) {
|
|
IOStatusHelper io("gethostbynamel", hostname.data());
|
|
Util::HostEnt result;
|
|
if (!Util::safe_gethostbyname(hostname.data(), result)) {
|
|
return false;
|
|
}
|
|
|
|
Array ret;
|
|
for (int i = 0 ; result.hostbuf.h_addr_list[i] != 0 ; i++) {
|
|
struct in_addr in = *(struct in_addr *)result.hostbuf.h_addr_list[i];
|
|
ret.append(String(Util::safe_inet_ntoa(in)));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant f_getprotobyname(CStrRef name) {
|
|
Lock lock(NetworkMutex);
|
|
|
|
struct protoent *ent = getprotobyname(name.data());
|
|
if (ent == NULL) {
|
|
return false;
|
|
}
|
|
return ent->p_proto;
|
|
}
|
|
|
|
Variant f_getprotobynumber(int number) {
|
|
Lock lock(NetworkMutex);
|
|
|
|
struct protoent *ent = getprotobynumber(number);
|
|
if (ent == NULL) {
|
|
return false;
|
|
}
|
|
return String(ent->p_name, CopyString);
|
|
}
|
|
|
|
Variant f_getservbyname(CStrRef service, CStrRef protocol) {
|
|
Lock lock(NetworkMutex);
|
|
|
|
struct servent *serv = getservbyname(service.data(), protocol.data());
|
|
if (serv == NULL) {
|
|
return false;
|
|
}
|
|
return ntohs(serv->s_port);
|
|
}
|
|
|
|
Variant f_getservbyport(int port, CStrRef protocol) {
|
|
Lock lock(NetworkMutex);
|
|
|
|
struct servent *serv = getservbyport(htons(port), protocol.data());
|
|
if (serv == NULL) {
|
|
return false;
|
|
}
|
|
return String(serv->s_name, CopyString);
|
|
}
|
|
|
|
Variant f_inet_ntop(CStrRef in_addr) {
|
|
int af = AF_INET;
|
|
if (in_addr.size() == 16) {
|
|
af = AF_INET6;
|
|
} else if (in_addr.size() != 4) {
|
|
raise_warning("Invalid in_addr value");
|
|
return false;
|
|
}
|
|
|
|
char buffer[40];
|
|
if (!inet_ntop(af, in_addr.data(), buffer, sizeof(buffer))) {
|
|
raise_warning("An unknown error occured");
|
|
return false;
|
|
}
|
|
return String(buffer, CopyString);
|
|
}
|
|
|
|
Variant f_inet_pton(CStrRef address) {
|
|
int af = AF_INET;
|
|
const char *saddress = address.data();
|
|
if (strchr(saddress, ':')) {
|
|
af = AF_INET6;
|
|
} else if (!strchr(saddress, '.')) {
|
|
raise_warning("Unrecognized address %s", saddress);
|
|
return false;
|
|
}
|
|
|
|
char buffer[17];
|
|
memset(buffer, 0, sizeof(buffer));
|
|
int ret = inet_pton(af, saddress, buffer);
|
|
if (ret <= 0) {
|
|
raise_warning("Unrecognized address %s", saddress);
|
|
return false;
|
|
}
|
|
|
|
return String(buffer, af == AF_INET ? 4 : 16, CopyString);
|
|
}
|
|
|
|
Variant f_ip2long(CStrRef ip_address) {
|
|
unsigned long int ip;
|
|
if (ip_address.empty() ||
|
|
(ip = inet_addr(ip_address.data())) == INADDR_NONE) {
|
|
/* the only special case when we should return -1 ourselves,
|
|
* because inet_addr() considers it wrong. We return 0xFFFFFFFF and
|
|
* not -1 or ~0 because of 32/64bit issues.
|
|
*/
|
|
if (ip_address == "255.255.255.255") {
|
|
return (int64_t)0xFFFFFFFF;
|
|
}
|
|
return false;
|
|
}
|
|
return (int64_t)ntohl(ip);
|
|
}
|
|
|
|
String f_long2ip(int proper_address) {
|
|
struct in_addr myaddr;
|
|
myaddr.s_addr = htonl(proper_address);
|
|
return Util::safe_inet_ntoa(myaddr);
|
|
}
|
|
|
|
/* just a hack to free resources allocated by glibc in __res_nsend()
|
|
* See also:
|
|
* res_thread_freeres() in glibc/resolv/res_init.c
|
|
* __libc_res_nsend() in resolv/res_send.c
|
|
* */
|
|
|
|
static void php_dns_free_res(struct __res_state *res) {
|
|
#if defined(__GLIBC__)
|
|
int ns;
|
|
for (ns = 0; ns < MAXNS; ns++) {
|
|
if (res->_u._ext.nsaddrs[ns] != NULL) {
|
|
free(res->_u._ext.nsaddrs[ns]);
|
|
res->_u._ext.nsaddrs[ns] = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool f_dns_check_record(CStrRef host, CStrRef type /* = null_string */) {
|
|
IOStatusHelper io("dns_check_record", host.data());
|
|
const char *stype;
|
|
if (type.empty()) {
|
|
stype = "MX";
|
|
} else {
|
|
stype = type.data();
|
|
}
|
|
if (host.empty()) {
|
|
throw_invalid_argument("host: [empty]");
|
|
}
|
|
|
|
int ntype;
|
|
if (!strcasecmp("A", stype)) ntype = DNS_T_A;
|
|
else if (!strcasecmp("NS", stype)) ntype = DNS_T_NS;
|
|
else if (!strcasecmp("MX", stype)) ntype = DNS_T_MX;
|
|
else if (!strcasecmp("PTR", stype)) ntype = DNS_T_PTR;
|
|
else if (!strcasecmp("ANY", stype)) ntype = DNS_T_ANY;
|
|
else if (!strcasecmp("SOA", stype)) ntype = DNS_T_SOA;
|
|
else if (!strcasecmp("TXT", stype)) ntype = DNS_T_TXT;
|
|
else if (!strcasecmp("CNAME", stype)) ntype = DNS_T_CNAME;
|
|
else if (!strcasecmp("AAAA", stype)) ntype = DNS_T_AAAA;
|
|
else if (!strcasecmp("SRV", stype)) ntype = DNS_T_SRV;
|
|
else if (!strcasecmp("NAPTR", stype)) ntype = DNS_T_NAPTR;
|
|
else if (!strcasecmp("A6", stype)) ntype = DNS_T_A6;
|
|
else {
|
|
throw_invalid_argument("type: %s", stype);
|
|
return false;
|
|
}
|
|
|
|
unsigned char ans[MAXPACKET];
|
|
struct __res_state *res;
|
|
res = ResolverInit::s_res.get()->getResolver();
|
|
if (res == NULL) {
|
|
return false;
|
|
}
|
|
int i = res_nsearch(res, host.data(), C_IN, ntype, ans, sizeof(ans));
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return (i >= 0);
|
|
}
|
|
|
|
bool f_checkdnsrr(CStrRef host, CStrRef type /* = null_string */) {
|
|
return f_dns_check_record(host, type);
|
|
}
|
|
|
|
typedef union {
|
|
HEADER qb1;
|
|
u_char qb2[65536];
|
|
} querybuf;
|
|
|
|
static const StaticString s_host("host");
|
|
static const StaticString s_type("type");
|
|
static const StaticString s_ip("ip");
|
|
static const StaticString s_pri("pri");
|
|
static const StaticString s_weight("weight");
|
|
static const StaticString s_port("port");
|
|
static const StaticString s_order("order");
|
|
static const StaticString s_pref("pref");
|
|
static const StaticString s_target("target");
|
|
static const StaticString s_cpu("cpu");
|
|
static const StaticString s_os("os");
|
|
static const StaticString s_txt("txt");
|
|
static const StaticString s_mname("mname");
|
|
static const StaticString s_rname("rname");
|
|
static const StaticString s_serial("serial");
|
|
static const StaticString s_refresh("refresh");
|
|
static const StaticString s_retry("retry");
|
|
static const StaticString s_expire("expire");
|
|
static const StaticString s_minimum_ttl("minimum-ttl");
|
|
static const StaticString s_ipv6("ipv6");
|
|
static const StaticString s_masklen("masklen");
|
|
static const StaticString s_chain("chain");
|
|
static const StaticString s_flags("flags");
|
|
static const StaticString s_services("services");
|
|
static const StaticString s_regex("regex");
|
|
static const StaticString s_replacement("replacement");
|
|
static const StaticString s_class("class");
|
|
static const StaticString s_ttl("ttl");
|
|
|
|
static const StaticString s_A("A");
|
|
static const StaticString s_MX("MX");
|
|
static const StaticString s_CNAME("CNAME");
|
|
static const StaticString s_NS("NS");
|
|
static const StaticString s_PTR("PTR");
|
|
static const StaticString s_HINFO("HINFO");
|
|
static const StaticString s_TXT("TXT");
|
|
static const StaticString s_SOA("SOA");
|
|
static const StaticString s_AAAA("AAAA");
|
|
static const StaticString s_A6("A6");
|
|
static const StaticString s_SRV("SRV");
|
|
static const StaticString s_NAPTR("NAPTR");
|
|
static const StaticString s_IN("IN");
|
|
|
|
static unsigned char *php_parserr(unsigned char *cp, querybuf *answer,
|
|
int type_to_fetch, bool store,
|
|
Array &subarray) {
|
|
unsigned short type, cls ATTRIBUTE_UNUSED, dlen;
|
|
unsigned long ttl;
|
|
int64_t n, i;
|
|
unsigned short s;
|
|
unsigned char *tp, *p;
|
|
char name[MAXHOSTNAMELEN];
|
|
int have_v6_break = 0, in_v6_break = 0;
|
|
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
|
|
GETSHORT(type, cp);
|
|
GETSHORT(cls, cp);
|
|
GETLONG(ttl, cp);
|
|
GETSHORT(dlen, cp);
|
|
if (type_to_fetch != T_ANY && type != type_to_fetch) {
|
|
cp += dlen;
|
|
return cp;
|
|
}
|
|
|
|
if (!store) {
|
|
cp += dlen;
|
|
return cp;
|
|
}
|
|
|
|
subarray.set(s_host, String(name, CopyString));
|
|
switch (type) {
|
|
case DNS_T_A:
|
|
subarray.set(s_type, s_A);
|
|
snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
|
|
subarray.set(s_ip, String(name, CopyString));
|
|
cp += dlen;
|
|
break;
|
|
case DNS_T_MX:
|
|
subarray.set(s_type, s_MX);
|
|
GETSHORT(n, cp);
|
|
subarray.set(s_pri, n);
|
|
/* no break; */
|
|
case DNS_T_CNAME:
|
|
if (type == DNS_T_CNAME) {
|
|
subarray.set(s_type, s_CNAME);
|
|
}
|
|
/* no break; */
|
|
case DNS_T_NS:
|
|
if (type == DNS_T_NS) {
|
|
subarray.set(s_type, s_NS);
|
|
}
|
|
/* no break; */
|
|
case DNS_T_PTR:
|
|
if (type == DNS_T_PTR) {
|
|
subarray.set(s_type, s_PTR);
|
|
}
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
subarray.set(s_target, String(name, CopyString));
|
|
break;
|
|
case DNS_T_HINFO:
|
|
/* See RFC 1010 for values */
|
|
subarray.set(s_type, s_HINFO);
|
|
n = *cp & 0xFF;
|
|
cp++;
|
|
subarray.set(s_cpu, String((const char *)cp, n, CopyString));
|
|
cp += n;
|
|
n = *cp & 0xFF;
|
|
cp++;
|
|
subarray.set(s_os, String((const char *)cp, n, CopyString));
|
|
cp += n;
|
|
break;
|
|
case DNS_T_TXT: {
|
|
int ll = 0;
|
|
|
|
subarray.set(s_type, s_TXT);
|
|
String s = String(dlen, ReserveString);
|
|
tp = (unsigned char *)s.mutableSlice().ptr;
|
|
|
|
while (ll < dlen) {
|
|
n = cp[ll];
|
|
memcpy(tp + ll , cp + ll + 1, n);
|
|
ll = ll + n + 1;
|
|
}
|
|
s.setSize(dlen);
|
|
cp += dlen;
|
|
|
|
subarray.set(s_txt, s);
|
|
break;
|
|
}
|
|
case DNS_T_SOA:
|
|
subarray.set(s_type, s_SOA);
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
subarray.set(s_mname, String(name, CopyString));
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
subarray.set(s_rname, String(name, CopyString));
|
|
GETLONG(n, cp);
|
|
subarray.set(s_serial, n);
|
|
GETLONG(n, cp);
|
|
subarray.set(s_refresh, n);
|
|
GETLONG(n, cp);
|
|
subarray.set(s_retry, n);
|
|
GETLONG(n, cp);
|
|
subarray.set(s_expire, n);
|
|
GETLONG(n, cp);
|
|
subarray.set(s_minimum_ttl, n);
|
|
break;
|
|
case DNS_T_AAAA:
|
|
tp = (unsigned char *)name;
|
|
for (i = 0; i < 8; i++) {
|
|
GETSHORT(s, cp);
|
|
if (s != 0) {
|
|
if (tp > (u_char *)name) {
|
|
in_v6_break = 0;
|
|
tp[0] = ':';
|
|
tp++;
|
|
}
|
|
tp += sprintf((char *)tp, "%x", s);
|
|
} else {
|
|
if (!have_v6_break) {
|
|
have_v6_break = 1;
|
|
in_v6_break = 1;
|
|
tp[0] = ':';
|
|
tp++;
|
|
} else if (!in_v6_break) {
|
|
tp[0] = ':';
|
|
tp++;
|
|
tp[0] = '0';
|
|
tp++;
|
|
}
|
|
}
|
|
}
|
|
if (have_v6_break && in_v6_break) {
|
|
tp[0] = ':';
|
|
tp++;
|
|
}
|
|
tp[0] = '\0';
|
|
subarray.set(s_type, s_AAAA);
|
|
subarray.set(s_ipv6, String(name, CopyString));
|
|
break;
|
|
case DNS_T_A6:
|
|
p = cp;
|
|
subarray.set(s_type, s_A6);
|
|
n = ((int)cp[0]) & 0xFF;
|
|
cp++;
|
|
subarray.set(s_masklen, n);
|
|
tp = (unsigned char *)name;
|
|
if (n > 15) {
|
|
have_v6_break = 1;
|
|
in_v6_break = 1;
|
|
tp[0] = ':';
|
|
tp++;
|
|
}
|
|
if (n % 16 > 8) {
|
|
/* Partial short */
|
|
if (cp[0] != 0) {
|
|
if (tp > (u_char *)name) {
|
|
in_v6_break = 0;
|
|
tp[0] = ':';
|
|
tp++;
|
|
}
|
|
sprintf((char *)tp, "%x", cp[0] & 0xFF);
|
|
} else {
|
|
if (!have_v6_break) {
|
|
have_v6_break = 1;
|
|
in_v6_break = 1;
|
|
tp[0] = ':';
|
|
tp++;
|
|
} else if (!in_v6_break) {
|
|
tp[0] = ':';
|
|
tp++;
|
|
tp[0] = '0';
|
|
tp++;
|
|
}
|
|
}
|
|
cp++;
|
|
}
|
|
for (i = (n + 8)/16; i < 8; i++) {
|
|
GETSHORT(s, cp);
|
|
if (s != 0) {
|
|
if (tp > (u_char *)name) {
|
|
in_v6_break = 0;
|
|
tp[0] = ':';
|
|
tp++;
|
|
}
|
|
tp += sprintf((char*)tp,"%x",s);
|
|
} else {
|
|
if (!have_v6_break) {
|
|
have_v6_break = 1;
|
|
in_v6_break = 1;
|
|
tp[0] = ':';
|
|
tp++;
|
|
} else if (!in_v6_break) {
|
|
tp[0] = ':';
|
|
tp++;
|
|
tp[0] = '0';
|
|
tp++;
|
|
}
|
|
}
|
|
}
|
|
if (have_v6_break && in_v6_break) {
|
|
tp[0] = ':';
|
|
tp++;
|
|
}
|
|
tp[0] = '\0';
|
|
subarray.set(s_ipv6, String(name, CopyString));
|
|
if (cp < p + dlen) {
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name,
|
|
(sizeof name) - 2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
subarray.set(s_chain, String(name, CopyString));
|
|
}
|
|
break;
|
|
case DNS_T_SRV:
|
|
subarray.set(s_type, s_SRV);
|
|
GETSHORT(n, cp);
|
|
subarray.set(s_pri, n);
|
|
GETSHORT(n, cp);
|
|
subarray.set(s_weight, n);
|
|
GETSHORT(n, cp);
|
|
subarray.set(s_port, n);
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
subarray.set(s_target, String(name, CopyString));
|
|
break;
|
|
case DNS_T_NAPTR:
|
|
subarray.set(s_type, s_NAPTR);
|
|
GETSHORT(n, cp);
|
|
subarray.set(s_order, n);
|
|
GETSHORT(n, cp);
|
|
subarray.set(s_pref, n);
|
|
n = (cp[0] & 0xFF);
|
|
subarray.set(s_flags, String((const char *)(++cp), n, CopyString));
|
|
cp += n;
|
|
n = (cp[0] & 0xFF);
|
|
subarray.set(s_services, String((const char *)(++cp), n, CopyString));
|
|
cp += n;
|
|
n = (cp[0] & 0xFF);
|
|
subarray.set(s_regex, String((const char *)(++cp), n, CopyString));
|
|
cp += n;
|
|
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
|
|
if (n < 0) {
|
|
return NULL;
|
|
}
|
|
cp += n;
|
|
subarray.set(s_replacement, String(name, CopyString));
|
|
break;
|
|
default:
|
|
cp += dlen;
|
|
}
|
|
|
|
subarray.set(s_class, s_IN);
|
|
subarray.set(s_ttl, (int)ttl);
|
|
return cp;
|
|
}
|
|
|
|
Variant f_dns_get_record(CStrRef hostname, int type /* = -1 */,
|
|
VRefParam authns /* = null */,
|
|
VRefParam addtl /* = null */) {
|
|
IOStatusHelper io("dns_get_record", hostname.data(), type);
|
|
if (type < 0) type = PHP_DNS_ALL;
|
|
if (type & ~PHP_DNS_ALL && type != PHP_DNS_ANY) {
|
|
raise_warning("Type '%d' not supported", type);
|
|
return false;
|
|
}
|
|
|
|
/* Initialize the return array */
|
|
Array ret;
|
|
authns = Array::Create();
|
|
addtl = Array::Create();
|
|
|
|
unsigned char *cp = NULL, *end = NULL;
|
|
int qd, an, ns = 0, ar = 0;
|
|
querybuf answer;
|
|
|
|
/* - We emulate an or'ed type mask by querying type by type.
|
|
* (Steps 0 - NUMTYPES-1 )
|
|
* If additional info is wanted we check again with DNS_T_ANY
|
|
* (step NUMTYPES / NUMTYPES+1 )
|
|
* store_results is used to skip storing the results retrieved in step
|
|
* NUMTYPES+1 when results were already fetched.
|
|
* - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY.
|
|
* (step NUMTYPES+1 )
|
|
*/
|
|
bool first_query = true;
|
|
bool store_results = true;
|
|
for (int t = (type == PHP_DNS_ANY ? (PHP_DNS_NUM_TYPES + 1) : 0);
|
|
t < PHP_DNS_NUM_TYPES + 2 || first_query; t++) {
|
|
first_query = false;
|
|
int type_to_fetch;
|
|
switch (t) {
|
|
case 0: type_to_fetch = type & PHP_DNS_A ? DNS_T_A : 0; break;
|
|
case 1: type_to_fetch = type & PHP_DNS_NS ? DNS_T_NS : 0; break;
|
|
case 2: type_to_fetch = type & PHP_DNS_CNAME ? DNS_T_CNAME : 0; break;
|
|
case 3: type_to_fetch = type & PHP_DNS_SOA ? DNS_T_SOA : 0; break;
|
|
case 4: type_to_fetch = type & PHP_DNS_PTR ? DNS_T_PTR : 0; break;
|
|
case 5: type_to_fetch = type & PHP_DNS_HINFO ? DNS_T_HINFO : 0; break;
|
|
case 6: type_to_fetch = type & PHP_DNS_MX ? DNS_T_MX : 0; break;
|
|
case 7: type_to_fetch = type & PHP_DNS_TXT ? DNS_T_TXT : 0; break;
|
|
case 8: type_to_fetch = type & PHP_DNS_AAAA ? DNS_T_AAAA : 0; break;
|
|
case 9: type_to_fetch = type & PHP_DNS_SRV ? DNS_T_SRV : 0; break;
|
|
case 10: type_to_fetch = type & PHP_DNS_NAPTR ? DNS_T_NAPTR : 0; break;
|
|
case 11: type_to_fetch = type & PHP_DNS_A6 ? DNS_T_A6 : 0; break;
|
|
case PHP_DNS_NUM_TYPES:
|
|
store_results = false;
|
|
continue;
|
|
default:
|
|
case (PHP_DNS_NUM_TYPES + 1):
|
|
type_to_fetch = DNS_T_ANY;
|
|
break;
|
|
}
|
|
if (!type_to_fetch) continue;
|
|
|
|
struct __res_state *res;
|
|
res = ResolverInit::s_res.get()->getResolver();
|
|
if (res == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int n = res_nsearch(res, hostname.data(), C_IN, type_to_fetch,
|
|
answer.qb2, sizeof answer);
|
|
if (n < 0) {
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
continue;
|
|
}
|
|
|
|
HEADER *hp;
|
|
cp = answer.qb2 + HFIXEDSZ;
|
|
end = answer.qb2 + n;
|
|
hp = (HEADER *)&answer;
|
|
qd = ntohs(hp->qdcount);
|
|
an = ntohs(hp->ancount);
|
|
ns = ntohs(hp->nscount);
|
|
ar = ntohs(hp->arcount);
|
|
|
|
/* Skip QD entries, they're only used by dn_expand later on */
|
|
while (qd-- > 0) {
|
|
n = dn_skipname(cp, end);
|
|
if (n < 0) {
|
|
raise_warning("Unable to parse DNS data received");
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return false;
|
|
}
|
|
cp += n + QFIXEDSZ;
|
|
}
|
|
|
|
/* YAY! Our real answers! */
|
|
while (an-- && cp && cp < end) {
|
|
Array retval;
|
|
cp = php_parserr(cp, &answer, type_to_fetch, store_results, retval);
|
|
if (!retval.empty() && store_results) {
|
|
ret.append(retval);
|
|
}
|
|
}
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
}
|
|
|
|
/* List of Authoritative Name Servers */
|
|
while (ns-- > 0 && cp && cp < end) {
|
|
Array retval;
|
|
cp = php_parserr(cp, &answer, DNS_T_ANY, true, retval);
|
|
if (!retval.empty()) {
|
|
authns.append(retval);
|
|
}
|
|
}
|
|
|
|
/* Additional records associated with authoritative name servers */
|
|
while (ar-- > 0 && cp && cp < end) {
|
|
Array retval;
|
|
cp = php_parserr(cp, &answer, DNS_T_ANY, true, retval);
|
|
if (!retval.empty()) {
|
|
addtl.append(retval);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool f_dns_get_mx(CStrRef hostname, VRefParam mxhosts,
|
|
VRefParam weights /* = null */) {
|
|
IOStatusHelper io("dns_get_mx", hostname.data());
|
|
int count, qdc;
|
|
unsigned short type, weight;
|
|
unsigned char ans[MAXPACKET];
|
|
char buf[MAXHOSTNAMELEN];
|
|
unsigned char *cp, *end;
|
|
|
|
mxhosts = Array::Create();
|
|
weights = Array::Create();
|
|
|
|
/* Go! */
|
|
struct __res_state *res;
|
|
res = ResolverInit::s_res.get()->getResolver();
|
|
if (res == NULL) {
|
|
return false;
|
|
}
|
|
|
|
int i = res_nsearch(res, hostname.data(), C_IN, DNS_T_MX,
|
|
(unsigned char*)&ans, sizeof(ans));
|
|
if (i < 0) {
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return false;
|
|
}
|
|
if (i > (int)sizeof(ans)) {
|
|
i = sizeof(ans);
|
|
}
|
|
HEADER *hp = (HEADER *)&ans;
|
|
cp = (unsigned char *)&ans + HFIXEDSZ;
|
|
end = (unsigned char *)&ans +i;
|
|
for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) {
|
|
if ((i = dn_skipname(cp, end)) < 0 ) {
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return false;
|
|
}
|
|
}
|
|
count = ntohs((unsigned short)hp->ancount);
|
|
while (--count >= 0 && cp < end) {
|
|
if ((i = dn_skipname(cp, end)) < 0 ) {
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return false;
|
|
}
|
|
cp += i;
|
|
GETSHORT(type, cp);
|
|
cp += INT16SZ + INT32SZ;
|
|
GETSHORT(i, cp);
|
|
if (type != DNS_T_MX) {
|
|
cp += i;
|
|
continue;
|
|
}
|
|
GETSHORT(weight, cp);
|
|
if ((i = dn_expand(ans, end, cp, buf, sizeof(buf)-1)) < 0) {
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return false;
|
|
}
|
|
cp += i;
|
|
mxhosts.append(String(buf, CopyString));
|
|
weights.append(weight);
|
|
}
|
|
res_nclose(res);
|
|
php_dns_free_res(res);
|
|
return true;
|
|
}
|
|
|
|
bool f_getmxrr(CStrRef hostname, VRefParam mxhosts,
|
|
VRefParam weight /* = uninit_null() */) {
|
|
return f_dns_get_mx(hostname, ref(mxhosts), weight);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// socket
|
|
|
|
/**
|
|
* f_fsockopen() and f_pfsockopen() are implemented in ext_socket.cpp.
|
|
*/
|
|
|
|
Variant f_socket_get_status(CObjRef stream) {
|
|
return f_stream_get_meta_data(stream);
|
|
}
|
|
|
|
bool f_socket_set_blocking(CObjRef stream, int mode) {
|
|
return f_stream_set_blocking(stream, mode);
|
|
}
|
|
|
|
bool f_socket_set_timeout(CObjRef stream, int seconds,
|
|
int microseconds /* = 0 */) {
|
|
return f_stream_set_timeout(stream, seconds, microseconds);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// http
|
|
|
|
void f_header(CStrRef str, bool replace /* = true */,
|
|
int http_response_code /* = 0 */) {
|
|
if (f_headers_sent()) {
|
|
raise_warning("Cannot modify header information - headers already sent");
|
|
}
|
|
|
|
String header = f_rtrim(str);
|
|
|
|
// new line safety check
|
|
// NOTE: PHP actually allows "\n " and "\n\t" to fall through. Is that bad
|
|
// for security?
|
|
if (header.find('\n') >= 0 || header.find('\r') >= 0) {
|
|
raise_warning("Header may not contain more than a single header, "
|
|
"new line detected");
|
|
return;
|
|
}
|
|
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport && header->size()) {
|
|
const char *header_line = header->data();
|
|
|
|
// handle single line of status code
|
|
if (header->size() >= 5 && strncasecmp(header_line, "HTTP/", 5) == 0) {
|
|
int code = 200;
|
|
for (const char *ptr = header_line; *ptr; ptr++) {
|
|
if (*ptr == ' ' && *(ptr + 1) != ' ') {
|
|
code = atoi(ptr + 1);
|
|
break;
|
|
}
|
|
}
|
|
if (code) {
|
|
transport->setResponse(code, "explicit_header");
|
|
}
|
|
return;
|
|
}
|
|
|
|
const char *colon_offset = strchr(header_line, ':');
|
|
String newHeader;
|
|
if (colon_offset) {
|
|
if (!strncasecmp(header_line, "Content-Type",
|
|
colon_offset - header_line)) {
|
|
const char *ptr = colon_offset+1, *mimetype = NULL;
|
|
while (*ptr == ' ') ptr++;
|
|
mimetype = ptr;
|
|
if (strncmp(mimetype, "text/", 5) == 0 &&
|
|
strstr(mimetype, "charset=") == NULL) {
|
|
newHeader = header + ";charset=utf-8";
|
|
}
|
|
}
|
|
}
|
|
if (replace) {
|
|
transport->replaceHeader(newHeader.empty() ? header : newHeader);
|
|
} else {
|
|
transport->addHeader(newHeader.empty() ? header : newHeader);
|
|
}
|
|
if (http_response_code) {
|
|
transport->setResponse(http_response_code,
|
|
"explicit_header_response_code");
|
|
}
|
|
}
|
|
}
|
|
|
|
Variant f_http_response_code(int response_code /*= 0 */) {
|
|
Transport *transport = g_context->getTransport();
|
|
if (!transport) {
|
|
raise_warning("Unable to access response code, no transport");
|
|
return false;
|
|
}
|
|
|
|
int old_code = transport->getResponseCode();
|
|
if (response_code) {
|
|
transport->setResponse(response_code, "explicit_header_response_code");
|
|
}
|
|
|
|
if (old_code) {
|
|
return old_code;
|
|
}
|
|
|
|
return response_code ? true : false;
|
|
}
|
|
|
|
Array f_headers_list() {
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport) {
|
|
HeaderMap headers;
|
|
transport->getResponseHeaders(headers);
|
|
Array ret;
|
|
for (HeaderMap::const_iterator iter = headers.begin();
|
|
iter != headers.end(); ++iter) {
|
|
const vector<string> &values = iter->second;
|
|
for (unsigned int i = 0; i < values.size(); i++) {
|
|
ret.append(String(iter->first + ": " + values[i]));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
return Array();
|
|
}
|
|
|
|
bool f_headers_sent(VRefParam file /* = null */, VRefParam line /* = null */) {
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport) {
|
|
file = String(transport->getFirstHeaderFile());
|
|
line = transport->getFirstHeaderLine();
|
|
return transport->headersSent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool f_header_register_callback(CVarRef callback) {
|
|
Transport *transport = g_context->getTransport();
|
|
if (!transport) {
|
|
// fail if there is no transport
|
|
return false;
|
|
}
|
|
if (transport->headersSent()) {
|
|
// fail if headers have already been sent
|
|
return false;
|
|
}
|
|
return transport->setHeaderCallback(callback);
|
|
}
|
|
|
|
void f_header_remove(CStrRef name /* = null_string */) {
|
|
if (f_headers_sent()) {
|
|
raise_warning("Cannot modify header information - headers already sent");
|
|
}
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport) {
|
|
if (name.isNull()) {
|
|
transport->removeAllHeaders();
|
|
} else {
|
|
transport->removeHeader(name.data());
|
|
}
|
|
}
|
|
}
|
|
|
|
int f_get_http_request_size() {
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport) {
|
|
return transport->getRequestSize();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool f_setcookie(CStrRef name, CStrRef value /* = null_string */,
|
|
int64_t expire /* = 0 */, CStrRef path /* = null_string */,
|
|
CStrRef domain /* = null_string */, bool secure /* = false */,
|
|
bool httponly /* = false */) {
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport) {
|
|
return transport->setCookie(name, value, expire, path, domain, secure,
|
|
httponly, true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool f_setrawcookie(CStrRef name, CStrRef value /* = null_string */,
|
|
int64_t expire /* = 0 */, CStrRef path /* = null_string */,
|
|
CStrRef domain /* = null_string */,
|
|
bool secure /* = false */,
|
|
bool httponly /* = false */) {
|
|
Transport *transport = g_context->getTransport();
|
|
if (transport) {
|
|
return transport->setCookie(name, value, expire, path, domain, secure,
|
|
httponly, false);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void f_define_syslog_variables() {
|
|
// do nothing, since all variables are defined as constants already
|
|
}
|
|
|
|
bool f_openlog(CStrRef ident, int option, int facility) {
|
|
openlog(ident.data(), option, facility);
|
|
return true;
|
|
}
|
|
|
|
bool f_closelog() {
|
|
closelog();
|
|
return true;
|
|
}
|
|
|
|
bool f_syslog(int priority, CStrRef message) {
|
|
syslog(priority, "%s", message.data());
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|