84b9d9a3a2
In HHVM (and HPHPc before it) we've been piggybacking resources on the KindOfObject machinery. At the language level, resource is considered to be a different type than object, and there are a number of differences in behavior between objects and resources (ex. resources don't allow for dynamic properties, resources don't work with the clone operator, the "(object)" cast behaves differently for resources vs. objects, etc). Piggybacking resources on the KindOfObject machinery has some downsides. Code that deals with KindOfObject values often needs to check if the value is a resource and go down a different code path. This makes things harder to maintain and harder to keep parity with Zend. Also, these extra branches hurt performance a little, and they make it harder for the JIT to do a good job in some cases when its generating machine code that operates on objects. This diff prepares the code base for a new KindOfResource type by adding a new "Resource" smart pointer type (currently a typedef for the Object smart pointer type) and it updates the C++ code and the idl files appropriately. This diff is essentially a cosmetic change and should not impact run time behavior. In the next diff (part 2) we'll actually add a new KindOfResource type, detach ResourceData from the ObjectData inheritence hierarchy, and provide a real implementation for the Resource smart pointer type (instead of just aliasing the Object smart pointer type).
1102 linhas
30 KiB
C++
1102 linhas
30 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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 "hphp/runtime/ext/ext_network.h"
|
|
#include "hphp/runtime/ext/ext_apc.h"
|
|
#include "hphp/runtime/ext/ext_string.h"
|
|
#include "hphp/runtime/base/runtime_option.h"
|
|
#include "hphp/runtime/base/server/server_stats.h"
|
|
#include "hphp/util/lock.h"
|
|
#include "hphp/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 "hphp/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(CResRef stream) {
|
|
return f_stream_get_meta_data(stream);
|
|
}
|
|
|
|
bool f_socket_set_blocking(CResRef stream, int mode) {
|
|
return f_stream_set_blocking(stream, mode);
|
|
}
|
|
|
|
bool f_socket_set_timeout(CResRef 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;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|