@@ -15,181 +15,10 @@
*/

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#else
# include "nameser.h"
#endif
#ifdef HAVE_ARPA_NAMESER_COMPAT_H
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_private.h"

/* Header format, from RFC 1035:
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ID |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QDCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ANCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | NSCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | ARCOUNT |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*
* AA, TC, RA, and RCODE are only set in responses. Brief description
* of the remaining fields:
* ID Identifier to match responses with queries
* QR Query (0) or response (1)
* Opcode For our purposes, always QUERY
* RD Recursion desired
* Z Reserved (zero)
* QDCOUNT Number of queries
* ANCOUNT Number of answers
* NSCOUNT Number of name server records
* ARCOUNT Number of additional records
*
* Question format, from RFC 1035:
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | |
* / QNAME /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QTYPE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | QCLASS |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*
* The query name is encoded as a series of labels, each represented
* as a one-byte length (maximum 63) followed by the text of the
* label. The list is terminated by a label of length zero (which can
* be thought of as the root domain).
*/

int ares_mkquery(const char *name, int dnsclass, int type, unsigned short id,
int rd, unsigned char **buf, int *buflen)
{
int len;
unsigned char *q;
const char *p;

/* Set our results early, in case we bail out early with an error. */
*buflen = 0;
*buf = NULL;

/* Compute the length of the encoded name so we can check buflen.
* Start counting at 1 for the zero-length label at the end. */
len = 1;
for (p = name; *p; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* If there are n periods in the name, there are n + 1 labels, and
* thus n + 1 length fields, unless the name is empty or ends with a
* period. So add 1 unless name is empty or ends with a period.
*/
if (*name && *(p - 1) != '.')
len++;

/* Immediately reject names that are longer than the maximum of 255
* bytes that's specified in RFC 1035 ("To simplify implementations,
* the total length of a domain name (i.e., label octets and label
* length octets) is restricted to 255 octets or less."). We aren't
* doing this just to be a stickler about RFCs. For names that are
* too long, 'dnscache' closes its TCP connection to us immediately
* (when using TCP) and ignores the request when using UDP, and
* BIND's named returns ServFail (TCP or UDP). Sending a request
* that we know will cause 'dnscache' to close the TCP connection is
* painful, since that makes any other outstanding requests on that
* connection fail. And sending a UDP request that we know
* 'dnscache' will ignore is bad because resources will be tied up
* until we time-out the request.
*/
if (len > MAXCDNAME)
return ARES_EBADNAME;

*buflen = len + HFIXEDSZ + QFIXEDSZ;
*buf = malloc(*buflen);
if (!*buf)
return ARES_ENOMEM;

/* Set up the header. */
q = *buf;
memset(q, 0, HFIXEDSZ);
DNS_HEADER_SET_QID(q, id);
DNS_HEADER_SET_OPCODE(q, QUERY);
if (rd) {
DNS_HEADER_SET_RD(q, 1);
}
else {
DNS_HEADER_SET_RD(q, 0);
}
DNS_HEADER_SET_QDCOUNT(q, 1);

/* A name of "." is a screw case for the loop below, so adjust it. */
if (strcmp(name, ".") == 0)
name++;

/* Start writing out the name after the header. */
q += HFIXEDSZ;
while (*name)
{
if (*name == '.')
return ARES_EBADNAME;

/* Count the number of bytes in this label. */
len = 0;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
if (len > MAXLABEL)
return ARES_EBADNAME;

/* Encode the length and copy the data. */
*q++ = (unsigned char)len;
for (p = name; *p && *p != '.'; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
*q++ = *p;
}

/* Go to the next label and repeat, unless we hit the end. */
if (!*p)
break;
name = p + 1;
}

/* Add the zero-length label at the end. */
*q++ = 0;

/* Finish off the question with the type and class. */
DNS_QUESTION_SET_TYPE(q, type);
DNS_QUESTION_SET_CLASS(q, dnsclass);

return ARES_SUCCESS;
return ares_create_query(name, dnsclass, type, id, rd, buf, buflen, 0);
}
@@ -23,9 +23,6 @@

#if defined(__INTEL_COMPILER) && defined(__unix__)

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -1,6 +1,6 @@

/* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright (C) 2008-2011 by Daniel Stenberg
* Copyright (C) 2008-2013 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -24,7 +24,7 @@

#include "ares.h"
#include "ares_data.h"
#include "inet_net_pton.h"
#include "ares_inet_net_pton.h"
#include "ares_private.h"


@@ -132,13 +132,15 @@ int ares_set_servers(ares_channel channel,
}

/* Incomming string format: host[:port][,host[:port]]... */
/* IPv6 addresses with ports require square brackets [fe80::1%lo0]:53 */
int ares_set_servers_csv(ares_channel channel,
const char* _csv)
{
size_t i;
char* csv = NULL;
char* ptr;
char* start_host;
int cc = 0;
int rv = ARES_SUCCESS;
struct ares_addr_node *servers = NULL;
struct ares_addr_node *last = NULL;
@@ -164,28 +166,53 @@ int ares_set_servers_csv(ares_channel channel,

start_host = csv;
for (ptr = csv; *ptr; ptr++) {
if (*ptr == ',') {
if (*ptr == ':') {
/* count colons to determine if we have an IPv6 number or IPv4 with
port */
cc++;
}
else if (*ptr == '[') {
/* move start_host if an open square bracket is found wrapping an IPv6
address */
start_host = ptr + 1;
}
else if (*ptr == ',') {
char* pp = ptr - 1;
char* p = ptr;
struct in_addr in4;
struct ares_in6_addr in6;
struct ares_addr_node *s = NULL;

*ptr = 0; /* null terminate host:port string */
/* Got an entry..see if port was specified. */
while (pp > start_host) {
if (*pp == ':')
break; /* yes */
if (!ISDIGIT(*pp)) {
/* Found end of digits before we found :, so wasn't a port */
pp = ptr;
break;
/* Got an entry..see if the port was specified. */
if (cc > 0) {
while (pp > start_host) {
/* a single close square bracket followed by a colon, ']:' indicates
an IPv6 address with port */
if ((*pp == ']') && (*p == ':'))
break; /* found port */
/* a single colon, ':' indicates an IPv4 address with port */
if ((*pp == ':') && (cc == 1))
break; /* found port */
if (!(ISDIGIT(*pp) || (*pp == ':'))) {
/* Found end of digits before we found :, so wasn't a port */
/* must allow ':' for IPv6 case of ']:' indicates we found a port */
pp = p = ptr;
break;
}
pp--;
p--;
}
if ((pp != start_host) && ((pp + 1) < ptr)) {
/* Found it. Parse over the port number */
/* when an IPv6 address is wrapped with square brackets the port
starts at pp + 2 */
if (*pp == ']')
p++; /* move p before ':' */
/* p will point to the start of the port */
(void)strtol(p, NULL, 10);
*pp = 0; /* null terminate host */
}
pp--;
}
if ((pp != start_host) && ((pp + 1) < ptr)) {
/* Found it. Parse over the port number */
(void)strtol(pp + 1, NULL, 10);
*pp = 0; /* null terminate host */
}
/* resolve host, try ipv4 first, rslt is in network byte order */
rv = ares_inet_pton(AF_INET, start_host, &in4);
@@ -221,6 +248,8 @@ int ares_set_servers_csv(ares_channel channel,
s->next = NULL;
if (last) {
last->next = s;
/* need to move last to maintain the linked list */
last = last->next;
}
else {
servers = s;
@@ -230,6 +259,7 @@ int ares_set_servers_csv(ares_channel channel,

/* Set up for next one */
start_host = ptr + 1;
cc = 0;
}
}

@@ -16,9 +16,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -41,8 +38,6 @@
# include <strings.h>
#endif

#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
@@ -141,6 +136,12 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen,
rr_len = DNS_RR_LEN(aptr);
rr_ttl = DNS_RR_TTL(aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
free(rr_name);
status = ARES_EBADRESP;
break;
}

if (rr_class == C_IN && rr_type == T_A
&& rr_len == sizeof(struct in_addr)
@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -42,15 +39,13 @@
# include <strings.h>
#endif

#include <stdlib.h>
#include <string.h>
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif

#include "ares.h"
#include "ares_dns.h"
#include "inet_net_pton.h"
#include "ares_inet_net_pton.h"
#include "ares_private.h"

int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
@@ -141,6 +136,12 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
rr_len = DNS_RR_LEN(aptr);
rr_ttl = DNS_RR_TTL(aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
free(rr_name);
status = ARES_EBADRESP;
break;
}

if (rr_class == C_IN && rr_type == T_AAAA
&& rr_len == sizeof(struct ares_in6_addr)
@@ -241,6 +242,8 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
for (i = 0; i < naddrs; i++)
hostent->h_addr_list[i] = (char *) &addrs[i];
hostent->h_addr_list[naddrs] = NULL;
if (!naddrs && addrs)
free(addrs);
*host = hostent;
return ARES_SUCCESS;
}
@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -38,8 +35,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_data.h"
@@ -105,6 +100,11 @@ ares_parse_mx_reply (const unsigned char *abuf, int alen,
rr_class = DNS_RR_CLASS (aptr);
rr_len = DNS_RR_LEN (aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
status = ARES_EBADRESP;
break;
}

/* Check if we are really looking at a MX record */
if (rr_class == C_IN && rr_type == T_MX)
@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -38,8 +35,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_data.h"
@@ -110,6 +105,11 @@ ares_parse_naptr_reply (const unsigned char *abuf, int alen,
rr_class = DNS_RR_CLASS (aptr);
rr_len = DNS_RR_LEN (aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
status = ARES_EBADRESP;
break;
}

/* Check if we are really looking at a NAPTR record */
if (rr_class == C_IN && rr_type == T_NAPTR)
@@ -20,9 +20,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -41,8 +38,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_private.h"
@@ -110,6 +105,12 @@ int ares_parse_ns_reply( const unsigned char* abuf, int alen,
rr_class = DNS_RR_CLASS( aptr );
rr_len = DNS_RR_LEN( aptr );
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
free(rr_name);
status = ARES_EBADRESP;
break;
}

if ( rr_class == C_IN && rr_type == T_NS )
{
@@ -16,9 +16,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -38,8 +35,6 @@
# include <strings.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_nowarn.h"
@@ -108,6 +103,12 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
rr_class = DNS_RR_CLASS(aptr);
rr_len = DNS_RR_LEN(aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
free(rr_name);
status = ARES_EBADRESP;
break;
}

if (rr_class == C_IN && rr_type == T_PTR
&& strcasecmp(rr_name, ptrname) == 0)
@@ -208,7 +209,7 @@ int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
status = ARES_ENOMEM;
}
for (i=0 ; i<aliascnt ; i++)
if (aliases[i])
if (aliases[i])
free(aliases[i]);
free(aliases);
if (hostname)
@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -38,8 +35,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_data.h"
@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -38,8 +35,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_data.h"
@@ -110,6 +105,11 @@ ares_parse_srv_reply (const unsigned char *abuf, int alen,
rr_class = DNS_RR_CLASS (aptr);
rr_len = DNS_RR_LEN (aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
status = ARES_EBADRESP;
break;
}

/* Check if we are really looking at a SRV record */
if (rr_class == C_IN && rr_type == T_SRV)
@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -42,9 +39,6 @@
# include <strings.h>
#endif

#include <stdlib.h>
#include <string.h>

#include "ares.h"
#include "ares_dns.h"
#include "ares_data.h"
@@ -54,7 +48,7 @@ int
ares_parse_txt_reply (const unsigned char *abuf, int alen,
struct ares_txt_reply **txt_out)
{
size_t substr_len, str_len;
size_t substr_len;
unsigned int qdcount, ancount, i;
const unsigned char *aptr;
const unsigned char *strptr;
@@ -112,27 +106,15 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen,
rr_class = DNS_RR_CLASS (aptr);
rr_len = DNS_RR_LEN (aptr);
aptr += RRFIXEDSZ;
if (aptr + rr_len > abuf + alen)
{
status = ARES_EBADRESP;
break;
}

/* Check if we are really looking at a TXT record */
if (rr_class == C_IN && rr_type == T_TXT)
{
/* Allocate storage for this TXT answer appending it to the list */
txt_curr = ares_malloc_data(ARES_DATATYPE_TXT_REPLY);
if (!txt_curr)
{
status = ARES_ENOMEM;
break;
}
if (txt_last)
{
txt_last->next = txt_curr;
}
else
{
txt_head = txt_curr;
}
txt_last = txt_curr;

/*
* There may be multiple substrings in a single TXT record. Each
* substring may be up to 255 characters in length, with a
@@ -141,36 +123,50 @@ ares_parse_txt_reply (const unsigned char *abuf, int alen,
* substrings contained therein.
*/

/* Compute total length to allow a single memory allocation */
strptr = aptr;
while (strptr < (aptr + rr_len))
{
substr_len = (unsigned char)*strptr;
txt_curr->length += substr_len;
strptr += substr_len + 1;
}

/* Including null byte */
txt_curr->txt = malloc (txt_curr->length + 1);
if (txt_curr->txt == NULL)
{
status = ARES_ENOMEM;
break;
}
if (strptr + substr_len + 1 > aptr + rr_len)
{
status = ARES_EBADRESP;
break;
}

/* Allocate storage for this TXT answer appending it to the list */
txt_curr = ares_malloc_data(ARES_DATATYPE_TXT_REPLY);
if (!txt_curr)
{
status = ARES_ENOMEM;
break;
}
if (txt_last)
{
txt_last->next = txt_curr;
}
else
{
txt_head = txt_curr;
}
txt_last = txt_curr;

txt_curr->record_start = strptr == aptr;
txt_curr->length = substr_len;
txt_curr->txt = malloc (substr_len + 1/* Including null byte */);
if (txt_curr->txt == NULL)
{
status = ARES_ENOMEM;
break;
}

++strptr;
memcpy ((char *) txt_curr->txt, strptr, substr_len);

/* Make sure we NULL-terminate */
txt_curr->txt[substr_len] = 0;

/* Step through the list of substrings, concatenating them */
str_len = 0;
strptr = aptr;
while (strptr < (aptr + rr_len))
{
substr_len = (unsigned char)*strptr;
strptr++;
memcpy ((char *) txt_curr->txt + str_len, strptr, substr_len);
str_len += substr_len;
strptr += substr_len;
}
/* Make sure we NULL-terminate */
*((char *) txt_curr->txt + txt_curr->length) = '\0';
}

/* Don't lose memory in the next iteration */
@@ -26,9 +26,6 @@
#define WIN32
#endif

#include <stdio.h>
#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
@@ -40,10 +37,6 @@
#define HAVE_WRITEV 1
#endif

#ifdef NETWARE
#include <time.h>
#endif

#define DEFAULT_TIMEOUT 5000 /* milliseconds */
#define DEFAULT_TRIES 4
#ifndef INADDR_NONE
@@ -113,6 +106,13 @@
# define writev(s,ptr,cnt) ares_writev(s,ptr,cnt)
#endif

/********* EDNS defines section ******/
#define EDNSPACKETSZ 1280 /* Reasonable UDP payload size, as suggested
in RFC2671 */
#define MAXENDSSZ 4096 /* Maximum (local) limit for edns packet size */
#define EDNSFIXEDSZ 11 /* Size of EDNS header */
/********* EDNS defines section ******/

struct ares_addr {
int family;
union {
@@ -260,6 +260,7 @@ struct ares_channeldata {
struct apattern *sortlist;
int nsort;
char *lookups;
int ednspsz;

/* For binding to local devices and/or IP addresses. Leave
* them null/zero for no binding.
@@ -317,7 +318,6 @@ long ares__timeoffset(struct timeval *now,
struct timeval *check);
/* returns ARES_SUCCESS if library has been initialized */
int ares_library_initialized(void);
void ares__rc4(rc4_key* key,unsigned char *buffer_ptr, int buffer_len);
void ares__send_query(ares_channel channel, struct query *query,
struct timeval *now);
void ares__close_sockets(ares_channel channel, struct server_state *server);
@@ -349,6 +349,7 @@ long ares__tvdiff(struct timeval t1, struct timeval t2);
libcurl lowlevel code from within library is ugly and only works when
c-ares is built and linked with a similarly curldebug-enabled libcurl,
but we do this anyway for convenience. */
#define HEADER_CURL_SETUP_ONCE_H
#include "../lib/memdebug.h"
#endif

Large diffs are not rendered by default.

@@ -16,9 +16,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -31,7 +28,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_private.h"
@@ -43,7 +39,7 @@ struct qquery {

static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf, int alen);

void ares__rc4(rc4_key* key, unsigned char *buffer_ptr, int buffer_len)
static void rc4(rc4_key* key, unsigned char *buffer_ptr, int buffer_len)
{
unsigned char x;
unsigned char y;
@@ -105,6 +101,13 @@ static unsigned short generate_unique_id(ares_channel channel)
return (unsigned short)id;
}

unsigned short ares__generate_new_id(rc4_key* key)
{
unsigned short r=0;
rc4(key, (unsigned char *)&r, sizeof(r));
return r;
}

void ares_query(ares_channel channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
{
@@ -114,8 +117,8 @@ void ares_query(ares_channel channel, const char *name, int dnsclass,

/* Compose the query. */
rd = !(channel->flags & ARES_FLAG_NORECURSE);
status = ares_mkquery(name, dnsclass, type, channel->next_id, rd, &qbuf,
&qlen);
status = ares_create_query(name, dnsclass, type, channel->next_id, rd, &qbuf,
&qlen, (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0);
if (status != ARES_SUCCESS)
{
if (qbuf != NULL) free(qbuf);
@@ -16,11 +16,6 @@

#include "ares_setup.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
@@ -16,9 +16,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -31,9 +28,6 @@
# include <arpa/nameser_compat.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "ares.h"
#include "ares_dns.h"
#include "ares_private.h"
@@ -42,7 +36,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
ares_callback callback, void *arg)
{
struct query *query;
int i;
int i, packetsz;
struct timeval now;

/* Verify that the query is at least long enough to hold the header. */
@@ -109,7 +103,10 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
query->server_info[i].skip_server = 0;
query->server_info[i].tcp_connection_generation = 0;
}
query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || qlen > PACKETSZ;

packetsz = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : PACKETSZ;
query->using_tcp = (channel->flags & ARES_FLAG_USEVC) || qlen > packetsz;

query->error_status = ARES_ECONNREFUSED;
query->timeouts = 0;

@@ -16,12 +16,10 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#include <time.h>

#include "ares.h"
#include "ares_private.h"

@@ -67,8 +65,10 @@ struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv,
*/
if (min_offset != -1)
{
nextstop.tv_sec = min_offset/1000;
nextstop.tv_usec = (min_offset%1000)*1000;
int ioffset = (min_offset > (long)INT_MAX) ? INT_MAX : (int)min_offset;

nextstop.tv_sec = ioffset/1000;
nextstop.tv_usec = (ioffset%1000)*1000;

if (!maxtv || ares__timedout(maxtv, &nextstop))
{
@@ -33,27 +33,27 @@
* author:
* Paul Vixie (ISC), June 1996
*/
int
ares_bitncmp(const void *l, const void *r, int n) {
unsigned int lb, rb;
int x, b;
int ares__bitncmp(const void *l, const void *r, int n)
{
unsigned int lb, rb;
int x, b;

b = n / 8;
x = memcmp(l, r, b);
if (x || (n % 8) == 0)
return (x);
b = n / 8;
x = memcmp(l, r, b);
if (x || (n % 8) == 0)
return (x);

lb = ((const unsigned char *)l)[b];
rb = ((const unsigned char *)r)[b];
for (b = n % 8; b > 0; b--) {
if ((lb & 0x80) != (rb & 0x80)) {
if (lb & 0x80)
return (1);
return (-1);
}
lb <<= 1;
rb <<= 1;
}
return (0);
lb = ((const unsigned char *)l)[b];
rb = ((const unsigned char *)r)[b];
for (b = n % 8; b > 0; b--) {
if ((lb & 0x80) != (rb & 0x80)) {
if (lb & 0x80)
return (1);
return (-1);
}
lb <<= 1;
rb <<= 1;
}
return (0);
}
#endif
@@ -2,7 +2,7 @@
#define __ARES_BITNCMP_H


/* Copyright (C) 2005 by Dominick Meglio
/* Copyright (C) 2005, 2013 by Dominick Meglio
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@@ -18,9 +18,9 @@
*/

#ifndef HAVE_BITNCMP
int ares_bitncmp(const void *l, const void *r, int n);
int ares__bitncmp(const void *l, const void *r, int n);
#else
#define ares_bitncmp(x,y,z) bitncmp(x,y,z)
#define ares__bitncmp(x,y,z) bitncmp(x,y,z)
#endif

#endif /* __ARES_BITNCMP_H */

This file was deleted.

@@ -18,9 +18,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -36,15 +33,10 @@
# include <arpa/nameser_compat.h>
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "ares.h"
#include "ares_ipv6.h"
#include "ares_nowarn.h"
#include "inet_net_pton.h"
#include "ares_inet_net_pton.h"


const struct ares_in6_addr ares_in6addr_any = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
@@ -448,4 +440,11 @@ int ares_inet_pton(int af, const char *src, void *dst)
return 0;
return (result > -1 ? 1 : -1);
}
#else /* HAVE_INET_PTON */
int ares_inet_pton(int af, const char *src, void *dst)
{
/* just relay this to the underlying function */
return inet_pton(af, src, dst);
}

#endif

This file was deleted.

@@ -17,9 +17,6 @@

#include "ares_setup.h"

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
@@ -35,15 +32,8 @@
# include <arpa/nameser_compat.h>
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "ares.h"
#include "ares_ipv6.h"
#include "inet_ntop.h"


#ifndef HAVE_INET_NTOP

@@ -69,13 +59,13 @@ static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size);
* Paul Vixie, 1996.
*/
const char *
ares_inet_ntop(int af, const void *src, char *dst, size_t size)
ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size)
{
switch (af) {
case AF_INET:
return (inet_ntop4(src, dst, size));
return (inet_ntop4(src, dst, (size_t)size));
case AF_INET6:
return (inet_ntop6(src, dst, size));
return (inet_ntop6(src, dst, (size_t)size));
default:
SET_ERRNO(EAFNOSUPPORT);
return (NULL);
@@ -205,4 +195,14 @@ inet_ntop6(const unsigned char *src, char *dst, size_t size)
strcpy(dst, tmp);
return (dst);
}
#endif

#else /* HAVE_INET_NTOP */

const char *
ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size)
{
/* just relay this to the underlying function */
return inet_ntop(af, src, dst, size);
}

#endif /* HAVE_INET_NTOP */

This file was deleted.

@@ -2,7 +2,7 @@
#define __SETUP_ONCE_H


/* Copyright (C) 2004 - 2012 by Daniel Stenberg et al
/* Copyright (C) 2004 - 2013 by Daniel Stenberg et al
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
@@ -76,6 +76,34 @@
#include <stdbool.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef __hpux
# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
# ifdef _APP32_64BIT_OFF_T
# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T
# undef _APP32_64BIT_OFF_T
# else
# undef OLD_APP32_64BIT_OFF_T
# endif
# endif
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif

#ifdef __hpux
# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
# ifdef OLD_APP32_64BIT_OFF_T
# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T
# undef OLD_APP32_64BIT_OFF_T
# endif
# endif
#endif


/*
* Definition of timeval struct for platforms that don't have it.
@@ -232,6 +260,8 @@ struct timeval {
# define sclose(x) closesocket((x))
#elif defined(HAVE_CLOSESOCKET_CAMEL)
# define sclose(x) CloseSocket((x))
#elif defined(HAVE_CLOSE_S)
# define sclose(x) close_s((x))
#else
# define sclose(x) close((x))
#endif
@@ -259,6 +289,18 @@ struct timeval {
#define TOLOWER(x) (tolower((int) ((unsigned char)x)))


/*
* 'bool' stuff compatible with HP-UX headers.
*/

#if defined(__hpux) && !defined(HAVE_BOOL_T)
typedef int bool;
# define false 0
# define true 1
# define HAVE_BOOL_T
#endif


/*
* 'bool' exists on platforms with <stdbool.h>, i.e. C99 platforms.
* On non-C99 platforms there's no bool, so define an enum for that.
@@ -0,0 +1,24 @@
{
"targets": [{
"target_name": "debugger-agent",
"type": "<(library)",
"include_dirs": [
"src",
"include",
"../v8/include",
"../uv/include",

# Private node.js folder and stuff needed to include from it
"../../src",
"../cares/include",
],
"direct_dependent_settings": {
"include_dirs": [
"include",
],
},
"sources": [
"src/agent.cc",
],
}],
}
@@ -0,0 +1,109 @@
// Copyright Fedor Indutny and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
#define DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_

#include "uv.h"
#include "v8.h"
#include "v8-debug.h"

namespace node {

// Forward declaration
class Environment;

namespace debugger {

// Forward declaration
class AgentMessage;

class Agent {
public:
explicit Agent(node::Environment* env);
~Agent();

typedef void (*DispatchHandler)(node::Environment* env);

// Start the debugger agent thread
bool Start(int port, bool wait);
// Listen for debug events
void Enable();
// Stop the debugger agent
void Stop();

inline void set_dispatch_handler(DispatchHandler handler) {
dispatch_handler_ = handler;
}

inline node::Environment* parent_env() const { return parent_env_; }
inline node::Environment* child_env() const { return child_env_; }

protected:
void InitAdaptor(Environment* env);

// Worker body
void WorkerRun();

static void ThreadCb(Agent* agent);
static void ParentSignalCb(uv_async_t* signal);
static void ChildSignalCb(uv_async_t* signal);
static void MessageHandler(const v8::Debug::Message& message);

// V8 API
static Agent* Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args);
static void NotifyListen(const v8::FunctionCallbackInfo<v8::Value>& args);
static void NotifyWait(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SendCommand(const v8::FunctionCallbackInfo<v8::Value>& args);

void EnqueueMessage(AgentMessage* message);

enum State {
kNone,
kRunning
};

// TODO(indutny): Verify that there are no races
State state_;

int port_;
bool wait_;

uv_sem_t start_sem_;
uv_mutex_t message_mutex_;
uv_async_t child_signal_;

uv_thread_t thread_;
node::Environment* parent_env_;
node::Environment* child_env_;
uv_loop_t child_loop_;
v8::Persistent<v8::Object> api_;

// QUEUE
void* messages_[2];

DispatchHandler dispatch_handler_;
};

} // namespace debugger
} // namespace node

#endif // DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
@@ -0,0 +1,191 @@
var assert = require('assert');
var net = require('net');
var util = require('util');
var Buffer = require('buffer').Buffer;

var Transform = require('stream').Transform;

exports.start = function start() {
var agent = new Agent();

// Do not let `agent.listen()` request listening from cluster master
var cluster = require('cluster');
cluster.isWorker = false;
cluster.isMaster = true;

agent.on('error', function(err) {
process._rawDebug(err.stack || err);
});

agent.listen(process._debugAPI.port, function() {
var addr = this.address();
process._rawDebug('Debugger listening on port %d', addr.port);
process._debugAPI.notifyListen();
});

// Just to spin-off events
// TODO(indutny): Figure out why node.cc isn't doing this
setImmediate(function() {
});

process._debugAPI.onclose = function() {
// We don't care about it, but it prevents loop from cleaning up gently
// NOTE: removeAllListeners won't work, as it doesn't call `removeListener`
process.listeners('SIGWINCH').forEach(function(fn) {
process.removeListener('SIGWINCH', fn);
});

agent.close();
};

// Not used now, but anyway
return agent;
};

function Agent() {
net.Server.call(this, this.onConnection);

this.first = true;
this.binding = process._debugAPI;

var self = this;
this.binding.onmessage = function(msg) {
self.clients.forEach(function(client) {
client.send({}, msg);
});
};

this.clients = [];
assert(this.binding, 'Debugger agent running without bindings!');
}
util.inherits(Agent, net.Server);

Agent.prototype.onConnection = function onConnection(socket) {
var c = new Client(this, socket);

c.start();
this.clients.push(c);

var self = this;
c.once('close', function() {
var index = self.clients.indexOf(c);
assert(index !== -1);
self.clients.splice(index, 1);
});
};

Agent.prototype.notifyWait = function notifyWait() {
if (this.first)
this.binding.notifyWait();
this.first = false;
};

function Client(agent, socket) {
Transform.call(this);
this._readableState.objectMode = true;

this.agent = agent;
this.binding = this.agent.binding;
this.socket = socket;

// Parse incoming data
this.state = 'headers';
this.headers = {};
this.buffer = '';
socket.pipe(this);

this.on('data', this.onCommand);

var self = this;
this.socket.on('close', function() {
self.destroy();
});
}
util.inherits(Client, Transform);

Client.prototype.destroy = function destroy(msg) {
this.socket.destroy();

this.emit('close');
};

Client.prototype._transform = function _transform(data, enc, cb) {
cb();

this.buffer += data;

while (true) {
if (this.state === 'headers') {
// Not enough data
if (!/\r\n/.test(this.buffer))
break;

if (/^\r\n/.test(this.buffer)) {
this.buffer = this.buffer.slice(2);
this.state = 'body';
continue;
}

// Match:
// Header-name: header-value\r\n
var match = this.buffer.match(/^([^:\s\r\n]+)\s*:\s*([^\s\r\n]+)\r\n/);
if (!match)
return this.destroy('Expected header, but failed to parse it');

this.headers[match[1].toLowerCase()] = match[2];

this.buffer = this.buffer.slice(match[0].length);
} else {
var len = this.headers['content-length'];
if (len === undefined)
return this.destroy('Expected content-length');

len = len | 0;
if (Buffer.byteLength(this.buffer) < len)
break;

this.push(new Command(this.headers, this.buffer.slice(0, len)));
this.state = 'headers';
this.buffer = this.buffer.slice(len);
this.headers = {};
}
}
};

Client.prototype.send = function send(headers, data) {
if (!data)
data = '';

var out = [];
Object.keys(headers).forEach(function(key) {
out.push(key + ': ' + headers[key]);
});
out.push('Content-Length: ' + Buffer.byteLength(data), '');

this.socket.cork();
this.socket.write(out.join('\r\n') + '\r\n');

if (data.length > 0)
this.socket.write(data);
this.socket.uncork();
};

Client.prototype.start = function start() {
this.send({
Type: 'connect',
'V8-Version': process.versions.v8,
'Protocol-Version': 1,
'Embedding-Host': 'node ' + process.version
});
};

Client.prototype.onCommand = function onCommand(cmd) {
this.binding.sendCommand(cmd.body);

this.agent.notifyWait();
};

function Command(headers, body) {
this.headers = headers;
this.body = body;
}
@@ -0,0 +1,347 @@
// Copyright Fedor Indutny and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "agent.h"
#include "debugger-agent.h"

#include "node.h"
#include "node_internals.h" // ARRAY_SIZE
#include "env.h"
#include "env-inl.h"
#include "v8.h"
#include "v8-debug.h"
#include "util.h"
#include "util-inl.h"
#include "queue.h"

#include <string.h>

namespace node {
namespace debugger {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Locker;
using v8::Object;
using v8::String;
using v8::Value;


Agent::Agent(Environment* env) : state_(kNone),
port_(5858),
wait_(false),
parent_env_(env),
child_env_(NULL),
dispatch_handler_(NULL) {
int err;

err = uv_sem_init(&start_sem_, 0);
CHECK_EQ(err, 0);

err = uv_mutex_init(&message_mutex_);
CHECK_EQ(err, 0);

QUEUE_INIT(&messages_);
}


Agent::~Agent() {
Stop();

uv_sem_destroy(&start_sem_);
uv_mutex_destroy(&message_mutex_);

// Clean-up messages
while (!QUEUE_EMPTY(&messages_)) {
QUEUE* q = QUEUE_HEAD(&messages_);
QUEUE_REMOVE(q);
AgentMessage* msg = ContainerOf(&AgentMessage::member, q);
delete msg;
}
}


bool Agent::Start(int port, bool wait) {
int err;

if (state_ == kRunning)
return false;

err = uv_loop_init(&child_loop_);
if (err != 0)
goto loop_init_failed;

// Interruption signal handler
err = uv_async_init(&child_loop_, &child_signal_, ChildSignalCb);
if (err != 0)
goto async_init_failed;
uv_unref(reinterpret_cast<uv_handle_t*>(&child_signal_));

port_ = port;
wait_ = wait;

err = uv_thread_create(&thread_,
reinterpret_cast<uv_thread_cb>(ThreadCb),
this);
if (err != 0)
goto thread_create_failed;

uv_sem_wait(&start_sem_);

state_ = kRunning;

return true;

thread_create_failed:
uv_close(reinterpret_cast<uv_handle_t*>(&child_signal_), NULL);

async_init_failed:
err = uv_loop_close(&child_loop_);
CHECK_EQ(err, 0);

loop_init_failed:
return false;
}


void Agent::Enable() {
v8::Debug::SetMessageHandler(MessageHandler);

// Assign environment to the debugger's context
// NOTE: The debugger context is created after `SetMessageHandler()` call
parent_env()->AssignToContext(v8::Debug::GetDebugContext());
}


void Agent::Stop() {
int err;

if (state_ != kRunning) {
return;
}

v8::Debug::SetMessageHandler(NULL);

// Send empty message to terminate things
EnqueueMessage(new AgentMessage(NULL, 0));

// Signal worker thread to make it stop
err = uv_async_send(&child_signal_);
CHECK_EQ(err, 0);

err = uv_thread_join(&thread_);
CHECK_EQ(err, 0);

uv_close(reinterpret_cast<uv_handle_t*>(&child_signal_), NULL);
uv_run(&child_loop_, UV_RUN_NOWAIT);

err = uv_loop_close(&child_loop_);
CHECK_EQ(err, 0);

state_ = kNone;
}


void Agent::WorkerRun() {
static const char* argv[] = { "node", "--debug-agent" };
Isolate* isolate = Isolate::New();
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);

HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);

Context::Scope context_scope(context);
Environment* env = CreateEnvironment(
isolate,
&child_loop_,
context,
ARRAY_SIZE(argv),
argv,
ARRAY_SIZE(argv),
argv);

child_env_ = env;

// Expose API
InitAdaptor(env);
LoadEnvironment(env);

CHECK_EQ(&child_loop_, env->event_loop());
uv_run(&child_loop_, UV_RUN_DEFAULT);

// Clean-up peristent
api_.Reset();

// Clean-up all running handles
env->CleanupHandles();

env->Dispose();
env = NULL;
}
isolate->Dispose();
}


void Agent::InitAdaptor(Environment* env) {
Isolate* isolate = env->isolate();
HandleScope scope(isolate);

// Create API adaptor
Local<FunctionTemplate> t = FunctionTemplate::New(isolate);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewFromUtf8(isolate, "DebugAPI"));

NODE_SET_PROTOTYPE_METHOD(t, "notifyListen", NotifyListen);
NODE_SET_PROTOTYPE_METHOD(t, "notifyWait", NotifyWait);
NODE_SET_PROTOTYPE_METHOD(t, "sendCommand", SendCommand);

Local<Object> api = t->GetFunction()->NewInstance();
api->SetAlignedPointerInInternalField(0, this);

api->Set(String::NewFromUtf8(isolate, "port"), Integer::New(isolate, port_));

env->process_object()->Set(String::NewFromUtf8(isolate, "_debugAPI"), api);
api_.Reset(env->isolate(), api);
}


Agent* Agent::Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args) {
void* ptr = args.Holder()->GetAlignedPointerFromInternalField(0);
return reinterpret_cast<Agent*>(ptr);
}


void Agent::NotifyListen(const FunctionCallbackInfo<Value>& args) {
Agent* a = Unwrap(args);

// Notify other thread that we are ready to process events
uv_sem_post(&a->start_sem_);
}


void Agent::NotifyWait(const FunctionCallbackInfo<Value>& args) {
Agent* a = Unwrap(args);

a->wait_ = false;

int err = uv_async_send(&a->child_signal_);
CHECK_EQ(err, 0);
}


void Agent::SendCommand(const FunctionCallbackInfo<Value>& args) {
Agent* a = Unwrap(args);
Environment* env = a->child_env();
HandleScope scope(env->isolate());

String::Value v(args[0]);

v8::Debug::SendCommand(a->parent_env()->isolate(), *v, v.length());
if (a->dispatch_handler_ != NULL)
a->dispatch_handler_(a->parent_env());
}


void Agent::ThreadCb(Agent* agent) {
agent->WorkerRun();
}


void Agent::ChildSignalCb(uv_async_t* signal) {
Agent* a = ContainerOf(&Agent::child_signal_, signal);
Isolate* isolate = a->child_env()->isolate();

HandleScope scope(isolate);
Local<Object> api = PersistentToLocal(isolate, a->api_);

uv_mutex_lock(&a->message_mutex_);
while (!QUEUE_EMPTY(&a->messages_)) {
QUEUE* q = QUEUE_HEAD(&a->messages_);
AgentMessage* msg = ContainerOf(&AgentMessage::member, q);

// Time to close everything
if (msg->data() == NULL) {
QUEUE_REMOVE(q);
delete msg;

MakeCallback(isolate, api, "onclose", 0, NULL);
break;
}

// Waiting for client, do not send anything just yet
// TODO(indutny): move this to js-land
if (a->wait_)
break;

QUEUE_REMOVE(q);
Local<Value> argv[] = {
String::NewFromTwoByte(isolate,
msg->data(),
String::kNormalString,
msg->length())
};

// Emit message
MakeCallback(isolate,
api,
"onmessage",
ARRAY_SIZE(argv),
argv);
delete msg;
}
uv_mutex_unlock(&a->message_mutex_);
}


void Agent::EnqueueMessage(AgentMessage* message) {
uv_mutex_lock(&message_mutex_);
QUEUE_INSERT_TAIL(&messages_, &message->member);
uv_mutex_unlock(&message_mutex_);
uv_async_send(&child_signal_);
}


void Agent::MessageHandler(const v8::Debug::Message& message) {
Isolate* isolate = message.GetIsolate();
Environment* env = Environment::GetCurrent(isolate);
Agent* a = env->debugger_agent();
CHECK_NE(a, NULL);
CHECK_EQ(isolate, a->parent_env()->isolate());

HandleScope scope(isolate);
Local<String> json = message.GetJSON();
String::Value v(json);

AgentMessage* msg = new AgentMessage(*v, v.length());
a->EnqueueMessage(msg);
}

} // namespace debugger
} // namespace node
@@ -0,0 +1,64 @@
// Copyright Fedor Indutny and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
#define DEPS_DEBUGGER_AGENT_SRC_AGENT_H_

#include "v8.h"
#include "v8-debug.h"
#include "queue.h"

#include <assert.h>
#include <string.h>

namespace node {
namespace debugger {

class AgentMessage {
public:
AgentMessage(uint16_t* val, int length) : length_(length) {
if (val == NULL) {
data_ = val;
} else {
data_ = new uint16_t[length];
memcpy(data_, val, length * sizeof(*data_));
}
}

~AgentMessage() {
delete[] data_;
data_ = NULL;
}

inline const uint16_t* data() const { return data_; }
inline int length() const { return length_; }

QUEUE member;

private:
uint16_t* data_;
int length_;
};

} // namespace debugger
} // namespace node

#endif // DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
@@ -1,11 +1,27 @@
/out/
core
tags
*.o
test
test_g
test_fast
url_parser
parsertrace
parsertrace_g
*.mk
*.Makefile
*.so
*.so.*
*.a


# Visual Studio uglies
*.suo
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.opensdf
*.ncrunchsolution*
*.sdf
*.vsp
*.psess
@@ -3,3 +3,5 @@
Ryan Dahl <ry@tinyclouds.org>
Salman Haq <salman.haq@asti-usa.com>
Simon Zimmermann <simonz05@gmail.com>
Thomas LE ROUX <thomas@november-eleven.fr> LE ROUX Thomas <thomas@procheo.fr>
Thomas LE ROUX <thomas@november-eleven.fr> Thomas LE ROUX <thomas@procheo.fr>
@@ -0,0 +1,13 @@
language: c

compiler:
- clang
- gcc

script:
- "make"

notifications:
email: false
irc:
- "irc.freenode.net#libuv"
@@ -28,10 +28,23 @@ Andre Caron <andre.l.caron@gmail.com>
Ivo Raisr <ivosh@ivosh.net>
James McLaughlin <jamie@lacewing-project.org>
David Gwynne <loki@animata.net>
LE ROUX Thomas <thomas@procheo.fr>
Thomas LE ROUX <thomas@november-eleven.fr>
Randy Rizun <rrizun@ortivawireless.com>
Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
Simon Zimmermann <simonz05@gmail.com>
Erik Dubbelboer <erik@dubbelboer.com>
Martell Malone <martellmalone@gmail.com>
Bertrand Paquet <bpaquet@octo.com>
BogDan Vatra <bogdan@kde.org>
Peter Faiman <peter@thepicard.org>
Corey Richardson <corey@octayn.net>
Tóth Tamás <tomika_nospam@freemail.hu>
Patrik Stutz <patrik.stutz@gmail.com>
Cam Swords <cam.swords@gmail.com>
Chris Dickinson <christopher.s.dickinson@gmail.com>
Uli Köhler <ukoehler@btronik.de>
Charlie Somerville <charlie@charliesomerville.com>
Fedor Indutny <fedor.indutny@gmail.com>
runner <runner.mei@gmail.com>
Alexis Campailla <alexis@janeasystems.com>
David Wragg <david@wragg.org>
@@ -1,17 +1,47 @@
# Copyright Joyent, Inc. and other Node contributors. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"')
SONAME ?= libhttp_parser.so.2.3

CC?=gcc
AR?=ar

CPPFLAGS += -I.
CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1 -DHTTP_PARSER_DEBUG=1
CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1
CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA)
CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0 -DHTTP_PARSER_DEBUG=0
CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0
CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA)

CFLAGS += -Wall -Wextra -Werror
CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA)
CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA)
CFLAGS_LIB = $(CFLAGS_FAST) -fPIC

LDFLAGS_LIB = $(LDFLAGS) -shared

ifneq (darwin,$(PLATFORM))
# TODO(bnoordhuis) The native SunOS linker expects -h rather than -soname...
LDFLAGS_LIB += -Wl,-soname=$(SONAME)
endif

test: test_g test_fast
./test_g
./test_fast
@@ -31,12 +61,6 @@ test_fast: http_parser.o test.o http_parser.h
test.o: test.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@

url_parser: http_parser_g.o url_parser.o
$(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o url_parser.o -o $@

url_parser.o: url_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c url_parser.c -o $@

http_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c

@@ -50,15 +74,32 @@ libhttp_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_LIB) -c http_parser.c -o libhttp_parser.o

library: libhttp_parser.o
$(CC) -shared -o libhttp_parser.so libhttp_parser.o
$(CC) $(LDFLAGS_LIB) -o $(SONAME) $<

package: http_parser.o
$(AR) rcs libhttp_parser.a http_parser.o

url_parser: http_parser.o contrib/url_parser.c
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o $@

url_parser_g: http_parser_g.o contrib/url_parser.c
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o $@

parsertrace: http_parser.o contrib/parsertrace.c
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o parsertrace

parsertrace_g: http_parser_g.o contrib/parsertrace.c
$(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o parsertrace_g

tags: http_parser.c http_parser.h test.c
ctags $^

clean:
rm -f *.o *.a test test_fast test_g url_parser http_parser.tar tags libhttp_parser.so libhttp_parser.o
rm -f *.o *.a tags test test_fast test_g \
http_parser.tar libhttp_parser.so.* \
url_parser url_parser_g parsertrace parsertrace_g

contrib/url_parser.c: http_parser.h
contrib/parsertrace.c: http_parser.h

.PHONY: clean package test-run test-run-timed test-valgrind
@@ -1,6 +1,8 @@
HTTP Parser
===========

[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)

This is a parser for HTTP messages written in C. It parses both requests and
responses. The parser is designed to be used in performance HTTP
applications. It does not make any syscalls nor allocations, it does not
@@ -34,38 +36,41 @@ Usage
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this for a request parser:
```c
http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser_settings settings;
settings.on_path = my_path_callback;
settings.on_header_field = my_header_field_callback;
/* ... */

http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
```

When data is received on the socket execute the parser and check for errors.

size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
```c
size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
recved = recv(fd, buf, len, 0);
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been recieved.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been recieved.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
```

HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
@@ -0,0 +1,156 @@
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
*
* Additional changes are licensed under the same terms as NGINX and
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/

/* Dump what the parser finds to stdout as it happen */

#include "http_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int on_message_begin(http_parser* _) {
(void)_;
printf("\n***MESSAGE BEGIN***\n\n");
return 0;
}

int on_headers_complete(http_parser* _) {
(void)_;
printf("\n***HEADERS COMPLETE***\n\n");
return 0;
}

int on_message_complete(http_parser* _) {
(void)_;
printf("\n***MESSAGE COMPLETE***\n\n");
return 0;
}

int on_url(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Url: %.*s\n", (int)length, at);
return 0;
}

int on_header_field(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header field: %.*s\n", (int)length, at);
return 0;
}

int on_header_value(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header value: %.*s\n", (int)length, at);
return 0;
}

int on_body(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Body: %.*s\n", (int)length, at);
return 0;
}

void usage(const char* name) {
fprintf(stderr,
"Usage: %s $type $filename\n"
" type: -x, where x is one of {r,b,q}\n"
" parses file as a Response, reQuest, or Both\n",
name);
exit(EXIT_FAILURE);
}

int main(int argc, char* argv[]) {
enum http_parser_type file_type;

if (argc != 3) {
usage(argv[0]);
}

char* type = argv[1];
if (type[0] != '-') {
usage(argv[0]);
}

switch (type[1]) {
/* in the case of "-", type[1] will be NUL */
case 'r':
file_type = HTTP_RESPONSE;
break;
case 'q':
file_type = HTTP_REQUEST;
break;
case 'b':
file_type = HTTP_BOTH;
break;
default:
usage(argv[0]);
}

char* filename = argv[2];
FILE* file = fopen(filename, "r");
if (file == NULL) {
perror("fopen");
return EXIT_FAILURE;
}

fseek(file, 0, SEEK_END);
long file_length = ftell(file);
if (file_length == -1) {
perror("ftell");
return EXIT_FAILURE;
}
fseek(file, 0, SEEK_SET);

char* data = malloc(file_length);
if (fread(data, 1, file_length, file) != (size_t)file_length) {
fprintf(stderr, "couldn't read entire file\n");
free(data);
return EXIT_FAILURE;
}

http_parser_settings settings;
memset(&settings, 0, sizeof(settings));
settings.on_message_begin = on_message_begin;
settings.on_url = on_url;
settings.on_header_field = on_header_field;
settings.on_header_value = on_header_value;
settings.on_headers_complete = on_headers_complete;
settings.on_body = on_body;
settings.on_message_complete = on_message_complete;

http_parser parser;
http_parser_init(&parser, file_type);
size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
free(data);

if (nparsed != (size_t)file_length) {
fprintf(stderr,
"Error: %s (%s)\n",
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}
File renamed without changes.
@@ -51,18 +51,10 @@
# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
#endif

#if HTTP_PARSER_DEBUG
#define SET_ERRNO(e) \
do { \
parser->http_errno = (e); \
parser->error_lineno = __LINE__; \
} while (0)
#else
#define SET_ERRNO(e) \
do { \
parser->http_errno = (e); \
} while(0)
#endif


/* Run the notify callback FOR, returning ER if it fails */
@@ -256,6 +248,7 @@ enum state
, s_res_http_minor
, s_res_first_status_code
, s_res_status_code
, s_res_status_start
, s_res_status
, s_res_line_almost_done

@@ -287,6 +280,9 @@ enum state

, s_header_field_start
, s_header_field
, s_header_value_discard_ws
, s_header_value_discard_ws_almost_done
, s_header_value_discard_lws
, s_header_value_start
, s_header_value
, s_header_value_lws
@@ -589,6 +585,7 @@ size_t http_parser_execute (http_parser *parser,
const char *header_value_mark = 0;
const char *url_mark = 0;
const char *body_mark = 0;
const char *status_mark = 0;

/* We're in an error state. Don't bother doing anything. */
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
@@ -635,14 +632,27 @@ size_t http_parser_execute (http_parser *parser,
case s_req_fragment:
url_mark = data;
break;
case s_res_status:
status_mark = data;
break;
}

for (p=data; p != data + len; p++) {
ch = *p;

if (PARSING_HEADER(parser->state)) {
++parser->nread;
/* Buffer overflow attack */
/* Don't allow the total size of the HTTP headers (including the status
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
* embedders against denial-of-service attacks where the attacker feeds
* us a never-ending header that the embedder keeps buffering.
*
* This check is arguably the responsibility of embedders but we're doing
* it on the embedder's behalf because most won't bother and this way we
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
* than any reasonable request or response so this should never affect
* day-to-day operation.
*/
if (parser->nread > HTTP_MAX_HEADER_SIZE) {
SET_ERRNO(HPE_HEADER_OVERFLOW);
goto error;
@@ -831,7 +841,7 @@ size_t http_parser_execute (http_parser *parser,
if (!IS_NUM(ch)) {
switch (ch) {
case ' ':
parser->state = s_res_status;
parser->state = s_res_status_start;
break;
case CR:
parser->state = s_res_line_almost_done;
@@ -857,18 +867,37 @@ size_t http_parser_execute (http_parser *parser,
break;
}

case s_res_status_start:
{
if (ch == CR) {
parser->state = s_res_line_almost_done;
break;
}

if (ch == LF) {
parser->state = s_header_field_start;
break;
}

MARK(status);
parser->state = s_res_status;
parser->index = 0;
break;
}

case s_res_status:
/* the human readable status. e.g. "NOT FOUND"
* we are not humans so just ignore this */
if (ch == CR) {
parser->state = s_res_line_almost_done;
CALLBACK_DATA(status);
break;
}

if (ch == LF) {
parser->state = s_header_field_start;
CALLBACK_DATA(status);
break;
}

break;

case s_res_line_almost_done:
@@ -1354,7 +1383,7 @@ size_t http_parser_execute (http_parser *parser,
}

if (ch == ':') {
parser->state = s_header_value_start;
parser->state = s_header_value_discard_ws;
CALLBACK_DATA(header_field);
break;
}
@@ -1375,28 +1404,28 @@ size_t http_parser_execute (http_parser *parser,
goto error;
}

case s_header_value_start:
{
case s_header_value_discard_ws:
if (ch == ' ' || ch == '\t') break;

MARK(header_value);

parser->state = s_header_value;
parser->index = 0;

if (ch == CR) {
parser->header_state = h_general;
parser->state = s_header_almost_done;
CALLBACK_DATA(header_value);
parser->state = s_header_value_discard_ws_almost_done;
break;
}

if (ch == LF) {
parser->state = s_header_field_start;
CALLBACK_DATA(header_value);
parser->state = s_header_value_discard_lws;
break;
}

/* FALLTHROUGH */

case s_header_value_start:
{
MARK(header_value);

parser->state = s_header_value;
parser->index = 0;

c = LOWER(ch);

switch (parser->header_state) {
@@ -1483,8 +1512,8 @@ size_t http_parser_execute (http_parser *parser,
t *= 10;
t += ch - '0';

/* Overflow? */
if (t < parser->content_length || t == ULLONG_MAX) {
/* Overflow? Test against a conservative limit for simplicity. */
if ((ULLONG_MAX - 10) / 10 < parser->content_length) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
@@ -1544,7 +1573,17 @@ size_t http_parser_execute (http_parser *parser,
STRICT_CHECK(ch != LF);

parser->state = s_header_value_lws;
break;
}

case s_header_value_lws:
{
if (ch == ' ' || ch == '\t') {
parser->state = s_header_value_start;
goto reexecute_byte;
}

/* finished the header */
switch (parser->header_state) {
case h_connection_keep_alive:
parser->flags |= F_CONNECTION_KEEP_ALIVE;
@@ -1559,19 +1598,29 @@ size_t http_parser_execute (http_parser *parser,
break;
}

parser->state = s_header_field_start;
goto reexecute_byte;
}

case s_header_value_discard_ws_almost_done:
{
STRICT_CHECK(ch != LF);
parser->state = s_header_value_discard_lws;
break;
}

case s_header_value_lws:
case s_header_value_discard_lws:
{
if (ch == ' ' || ch == '\t')
parser->state = s_header_value_start;
else
{
if (ch == ' ' || ch == '\t') {
parser->state = s_header_value_discard_ws;
break;
} else {
/* header value was empty */
MARK(header_value);
parser->state = s_header_field_start;
CALLBACK_DATA_NOADVANCE(header_value);
goto reexecute_byte;
}
break;
}

case s_headers_almost_done:
@@ -1756,8 +1805,8 @@ size_t http_parser_execute (http_parser *parser,
t *= 16;
t += unhex_val;

/* Overflow? */
if (t < parser->content_length || t == ULLONG_MAX) {
/* Overflow? Test against a conservative limit for simplicity. */
if ((ULLONG_MAX - 16) / 16 < parser->content_length) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
@@ -1851,12 +1900,14 @@ size_t http_parser_execute (http_parser *parser,
assert(((header_field_mark ? 1 : 0) +
(header_value_mark ? 1 : 0) +
(url_mark ? 1 : 0) +
(body_mark ? 1 : 0)) <= 1);
(body_mark ? 1 : 0) +
(status_mark ? 1 : 0)) <= 1);

CALLBACK_DATA_NOADVANCE(header_field);
CALLBACK_DATA_NOADVANCE(header_value);
CALLBACK_DATA_NOADVANCE(url);
CALLBACK_DATA_NOADVANCE(body);
CALLBACK_DATA_NOADVANCE(status);

return len;

@@ -1987,7 +2038,7 @@ http_parse_host_char(enum http_host_state s, const char ch) {

/* FALLTHROUGH */
case s_http_host_v6_start:
if (IS_HEX(ch) || ch == ':') {
if (IS_HEX(ch) || ch == ':' || ch == '.') {
return s_http_host_v6;
}

@@ -2192,3 +2243,15 @@ http_parser_pause(http_parser *parser, int paused) {
assert(0 && "Attempting to pause parser in error state");
}
}

int
http_body_is_final(const struct http_parser *parser) {
return parser->state == s_message_done;
}

unsigned long
http_parser_version(void) {
return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
HTTP_PARSER_VERSION_MINOR * 0x00100 |
HTTP_PARSER_VERSION_PATCH * 0x00001;
}
@@ -12,6 +12,7 @@
# RuntimeLibrary MUST MATCH across the entire project
'Debug': {
'defines': [ 'DEBUG', '_DEBUG' ],
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 1, # static debug
@@ -20,6 +21,7 @@
},
'Release': {
'defines': [ 'NDEBUG' ],
'cflags': [ '-Wall', '-Wextra', '-O3' ],
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': 0, # static release
@@ -51,6 +53,7 @@
'type': 'static_library',
'include_dirs': [ '.' ],
'direct_dependent_settings': {
'defines': [ 'HTTP_PARSER_STRICT=0' ],
'include_dirs': [ '.' ],
},
'defines': [ 'HTTP_PARSER_STRICT=0' ],
@@ -69,11 +72,40 @@
},

{
'target_name': 'test',
'target_name': 'http_parser_strict',
'type': 'static_library',
'include_dirs': [ '.' ],
'direct_dependent_settings': {
'defines': [ 'HTTP_PARSER_STRICT=1' ],
'include_dirs': [ '.' ],
},
'defines': [ 'HTTP_PARSER_STRICT=1' ],
'sources': [ './http_parser.c', ],
'conditions': [
['OS=="win"', {
'msvs_settings': {
'VCCLCompilerTool': {
# Compile as C++. http_parser.c is actually C99, but C++ is
# close enough in this case.
'CompileAs': 2,
},
},
}]
],
},

{
'target_name': 'test-nonstrict',
'type': 'executable',
'dependencies': [ 'http_parser' ],
'sources': [ 'test.c' ]
},

{
'target_name': 'test-strict',
'type': 'executable',
'dependencies': [ 'http_parser_strict' ],
'sources': [ 'test.c' ]
}
]
}

@@ -24,12 +24,15 @@
extern "C" {
#endif

#define HTTP_PARSER_VERSION_MAJOR 1
#define HTTP_PARSER_VERSION_MINOR 0
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 3
#define HTTP_PARSER_VERSION_PATCH 0

#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
#include <BaseTsd.h>
#include <stddef.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
@@ -38,8 +41,6 @@ typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef SIZE_T size_t;
typedef SSIZE_T ssize_t;
#else
#include <stdint.h>
#endif
@@ -51,14 +52,6 @@ typedef SSIZE_T ssize_t;
# define HTTP_PARSER_STRICT 1
#endif

/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
* the error reporting facility.
*/
#ifndef HTTP_PARSER_DEBUG
# define HTTP_PARSER_DEBUG 0
#endif


/* Maximium header size allowed */
#define HTTP_MAX_HEADER_SIZE (80*1024)

@@ -77,7 +70,7 @@ typedef struct http_parser_settings http_parser_settings;
* chunked' headers that indicate the presence of a body.
*
* http_data_cb does not return data chunks. It will be call arbitrarally
* many times for each string. E.G. you might get 10 callbacks for "on_path"
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
@@ -156,6 +149,7 @@ enum flags
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
@@ -196,42 +190,31 @@ enum http_errno {
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)

/* Get the line number that generated the current error */
#if HTTP_PARSER_DEBUG
#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
#else
#define HTTP_PARSER_ERRNO_LINE(p) 0
#endif


struct http_parser {
/** PRIVATE **/
unsigned char type : 2; /* enum http_parser_type */
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
unsigned char state; /* enum state from http_parser.c */
unsigned char header_state; /* enum header_state from http_parser.c */
unsigned char index; /* index into current matcher */
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 8; /* enum state from http_parser.c */
unsigned int header_state : 8; /* enum header_state from http_parser.c */
unsigned int index : 8; /* index into current matcher */

uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */

/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned short status_code; /* responses only */
unsigned char method; /* requests only */
unsigned char http_errno : 7;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;

/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned char upgrade : 1;

#if HTTP_PARSER_DEBUG
uint32_t error_lineno;
#endif
unsigned int upgrade : 1;

/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
@@ -241,6 +224,7 @@ struct http_parser {
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
@@ -279,6 +263,18 @@ struct http_parser_url {
};


/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, version);
*/
unsigned long http_parser_version(void);

void http_parser_init(http_parser *parser, enum http_parser_type type);


@@ -313,6 +309,9 @@ int http_parser_parse_url(const char *buf, size_t buflen,
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);

/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);

#ifdef __cplusplus
}
#endif