Пример #1
0
/*void
insertParentProxy(ConfigVariablePtr var,AtomPcharKey=(unsigned char*)key;tr av)          //add
{
    AtomPtr atom;
    atom=*var->value.a;
    while(1){
       //if(!atom->next)break;
       if(strcmp(atom->string,av->string)==0)return;
       if(!atom->next)break;
       atom=atom->next;
    }
    av->next=atom->next;
    atom->next=av;
    //av->next=*var->value.a;
    //*var->value.a=av;
    return;
}*/
static
int
parseAtom1(char *buf, int offset, AtomPtr *value_return, int insensitive)
{
    int y0, i, j, k;
    AtomPtr atom;
    int escape = 0;
    char *s;

    i = offset;
    if(buf[i] != '\0') {
        y0 = i;
        i++;
        while(buf[i] != '\"' && buf[i] != '\n' && buf[i] != '\0' && buf[i]!=',') {
            if(buf[i] == '\\' && buf[i + 1] != '\0') {
                escape = 1;
                i += 2;
            } else
                i++;
        }
        //if(buf[i] != '\0')
            //return -1;
        j = i ;
    } else {
        y0 = i;
        while(letter(buf[i]) || digit(buf[i]) || 
              buf[i] == '_' || buf[i] == '-' || buf[i] == '~' ||
              buf[i] == '.' || buf[i] == ':' || buf[i] == '/')
            i++;
        j = i;
    }

    if(escape) {
        s = malloc(i - y0);
        if(buf == NULL) return -1;
        k = 0;
        j = y0;
        while(j < i) {
            if(buf[j] == '\\' && j <= i - 2) {
                s[k++] = buf[j + 1];
                j += 2;
            } else
                s[k++] = buf[j++];
        }
        if(insensitive)
            atom = internAtomLowerN(s, k);
        else
            atom = internAtomN(s, k);
        free(s);
        j++;
    } else {
        if(insensitive)
            atom = internAtomLowerN(buf + y0, i - y0);
        else
            atom = internAtomN(buf + y0, i - y0);
    }
    *value_return = atom;
    return j;
}
Пример #2
0
AtomPtr
internAtomF(const char *format, ...)
{
    char *s;
    char buf[150];
    int n;
    va_list args;
    AtomPtr atom = NULL;

    va_start(args, format);
    n = vsnprintf(buf, 150, format, args);
    va_end(args);
    if(n >= 0 && n < 150) {
        atom = internAtomN(buf, n);
    } else {
        va_start(args, format);
        s = vsprintf_a(format, args);
        va_end(args);
        if(s != NULL) {
            atom = internAtom(s);
            free(s);
        }
    }
    return atom;
}
Пример #3
0
AtomPtr
rfc2732(AtomPtr name)
{
    char buf[38];
    int rc;
    AtomPtr a = NULL;

    if(name->length < 38 && 
       name->string[0] == '[' && name->string[name->length - 1] == ']') {
        struct in6_addr in6a;
        memcpy(buf, name->string + 1, name->length - 2);
        buf[name->length - 2] = '\0';
        rc = inet_pton(AF_INET6, buf, &in6a);
        if(rc == 1) {
            char s[1 + sizeof(HostAddressRec)];
            memset(s, 0, sizeof(s));
            s[0] = DNS_A;
            s[1] = 6;
            memcpy(s + 2, &in6a, 16);
            a = internAtomN(s, 1 + sizeof(HostAddressRec));
            if(a == NULL)
                return NULL;
        }
    }
    return a;
}
Пример #4
0
int atomSplit(AtomPtr atom, char c, AtomPtr * return1, AtomPtr * return2)
{
	char *p;
	AtomPtr atom1, atom2;
	p = memchr(atom->string, c, atom->length);
	if (p == NULL)
		return 0;
	atom1 = internAtomN(atom->string, p - atom->string);
	if (atom1 == NULL)
		return -ENOMEM;
	atom2 = internAtomN(p + 1, atom->length - (p + 1 - atom->string));
	if (atom2 == NULL) {
		releaseAtom(atom1);
		return -ENOMEM;
	}
	*return1 = atom1;
	*return2 = atom2;
	return 1;
}
Пример #5
0
int insertClientProxy(char *buf,int i)  //humeng add 11.4.23
{
    ClientProxyPtr node,cltPrxs;
    AtomPtr temp,addr,key;
    int x,y,rc;
    if(!buf[i]=='=') return(0);
    i++;
    skipBlank(buf,i);
    x=i;
    while(!(buf[i] == '\n' || buf[i] == '\0' || buf[i] == '#'))
    {
        skipBlank(buf,i);
        i++;
    }
    if(i>x+1)
    temp=internAtomN(buf+x,i-x-1);
    rc = atomSplit(temp, ':', &addr, &key);
    if(rc <= 0) {
        do_log(L_ERROR, "Couldn't parse parentProxy.");
        return -1;
    }
    node=malloc(sizeof(ClientProxyRec));
    node->next=NULL;
    node->addr=addr;
    node->key=key;
    node->keyHash=keyHasher(key->string, key->length);
    if(clientProxys==NULL)
    {
        clientProxys=node;
        return 1;
    }else
    {
        cltPrxs=clientProxys;
        while(cltPrxs)
        {
             if(strcmp(cltPrxs->addr->string,node->addr->string)==0)
             {
                  cltPrxs->key=node->key;
                  cltPrxs->keyHash=node->keyHash;
                  return 1;
             }
             if(cltPrxs->next==NULL)
             {
                 cltPrxs->next=node;
                 return 1;
             }
             cltPrxs=cltPrxs->next;
        }
    }
        return 1;

}
Пример #6
0
AtomPtr
atomCat(AtomPtr atom, const char *string)
{
    char buf[128];
    char *s = buf;
    AtomPtr newAtom;
    int n = strlen(string);
    if(atom->length + n > 128) {
        s = malloc(atom->length + n + 1);
        if(s == NULL)
            return NULL;
    }
    memcpy(s, atom->string, atom->length);
    memcpy(s + atom->length, string, n);
    newAtom = internAtomN(s, atom->length + n);
    if(s != buf) free(s);
    return newAtom;
}
Пример #7
0
int getPassword(char *buf,int i)  //added by yangkun
{
    AtomPtr temp;
    int x;
    if(!buf[i]=='=') return -1;
    i++;
    x=skipBlank(buf,i);
    while(!(buf[i] == '\n' || buf[i] == '\0' || buf[i] == '#'))
    {
        skipBlank(buf,i);
        i++;
    }
    if(i>x+1)
    {
        temp = internAtomN(buf+x,i-x-1);
        password = temp->string;
    }
    return 0;
}
Пример #8
0
static AtomPtr
internAtomErrorV(int e, const char *f, va_list args)
{

    char *es = pstrerror(e);
    AtomPtr atom;
    char *s1, *s2;
    int n, rc;
    va_list args_copy;

    if(f) {
        va_copy(args_copy, args);
        s1 = vsprintf_a(f, args_copy);
        va_end(args_copy);
        if(s1 == NULL)
            return NULL;
        n = strlen(s1);
    } else {
        s1 = NULL;
        n = 0;
    }

    s2 = malloc(n + 70);
    if(s2 == NULL) {
        free(s1);
        return NULL;
    }
    if(s1) {
        strcpy(s2, s1);
        free(s1);
    }

    rc = snprintf(s2 + n, 69, f ? ": %s" : "%s", es);
    if(rc < 0 || rc >= 69) {
        free(s2);
        return NULL;
    }

    atom = internAtomN(s2, n + rc);
    free(s2);
    return atom;
}
Пример #9
0
int
httpSpecialDoSide(HTTPRequestPtr requestor)
{
    HTTPConnectionPtr client = requestor->connection;

    if(client->reqlen - client->reqbegin >= client->bodylen) {
        AtomPtr data;
        data = internAtomN(client->reqbuf + client->reqbegin,
                           client->reqlen - client->reqbegin);
        client->reqbegin = 0;
        client->reqlen = 0;
        if(data == NULL) {
            do_log(L_ERROR, "Couldn't allocate data.\n");
            httpClientError(requestor, 500,
                            internAtom("Couldn't allocate data"));
            return 1;
        }
        httpSpecialDoSideFinish(data, requestor);
        return 1;
    }

    if(client->reqlen - client->reqbegin >= CHUNK_SIZE) {
        httpClientError(requestor, 500, internAtom("POST too large"));
        return 1;
    }

    if(client->reqbegin > 0 && client->reqlen > client->reqbegin) {
        memmove(client->reqbuf, client->reqbuf + client->reqbegin,
                client->reqlen - client->reqbegin);
    }
    client->reqlen -= client->reqbegin;
    client->reqbegin = 0;

    do_stream(IO_READ | IO_NOTNOW, client->fd,
              client->reqlen, client->reqbuf, CHUNK_SIZE,
              httpSpecialClientSideHandler, client);
    return 1;
}
Пример #10
0
AtomPtr
internAtomLowerN(const char *string, int n)
{
    char *s;
    char buf[100];
    AtomPtr atom;

    if(n < 0 || n >= 50000)
        return NULL;

    if(n < 100) {
        s = buf;
    } else {
        s = malloc(n);
        if(s == NULL)
            return NULL;
    }

    lwrcpy(s, string, n);
    atom = internAtomN(s, n);
    if(s != buf) free(s);
    return atom;
}
Пример #11
0
int setAddress(char *buf,int i)  //added by yangkun
{
    AtomPtr temp1, temp2;
    int x;
    if(!buf[i]=='=') return -1;
    i++;
    x=skipBlank(buf,i);
    while(!(buf[i] == '\n' || buf[i] == '\0' || buf[i] == '#'))
    {
        skipBlank(buf,i);
        i++;
    }
    if(i>x+1)
    {
        temp1 = internAtomN(buf+x,i-x-1);
        temp2 = internAtom("http://");
        temp2 = atomCat(temp2, temp1->string);
        temp2 = atomCat(temp2, "/heartbeat.php");
        releaseAtom(temp1);
        address = temp2->string;
    }
    return 0;
}
Пример #12
0
int httpSpecialDoSideFinish(AtomPtr data, HTTPRequestPtr requestor)
{
	ObjectPtr object = requestor->object;

	if (matchUrl("/s_server/config", object)) {
		AtomListPtr list = NULL;
		int i, rc;

		if (disableConfiguration) {
			abortObject(object, 403,
				    internAtom("Action not allowed"));
			goto out;
		}

		list = urlDecode(data->string, data->length);
		if (list == NULL) {
			abortObject(object, 400,
				    internAtom
				    ("Couldn't parse variable to set"));
			goto out;
		}
		for (i = 0; i < list->length; i++) {
			rc = parseConfigLine(list->list[i]->string, NULL, 0, 1);
			if (rc < 0) {
				abortObject(object, 400,
					    rc == -1 ?
					    internAtom
					    ("Couldn't parse variable to set") :
					    internAtom
					    ("Variable is not settable"));
				destroyAtomList(list);
				goto out;
			}
		}
		destroyAtomList(list);
		object->date = current_time.tv_sec;
		object->age = current_time.tv_sec;
		object->headers = internAtom("\r\nLocation: /s_server/config?");
		object->code = 303;
		object->message = internAtom("Done");
		object->flags &= ~OBJECT_INITIAL;
		object->length = 0;
	} else if (matchUrl("/s_server/status", object)) {
		AtomListPtr list = NULL;
		int i;

		if (disableConfiguration) {
			abortObject(object, 403,
				    internAtom("Action not allowed"));
			goto out;
		}

		list = urlDecode(data->string, data->length);
		if (list == NULL) {
			abortObject(object, 400,
				    internAtom("Couldn't parse action"));
			goto out;
		}
		for (i = 0; i < list->length; i++) {
			char *equals = memchr(list->list[i]->string, '=',
					      list->list[i]->length);
			AtomPtr name =
			    equals ? internAtomN(list->list[i]->string,
						 equals -
						 list->
						 list[i]->string) :
			    retainAtom(list->list[i]);
			if (name == atomInitForbidden)
				initForbidden();
			else if (name == atomReopenLog)
				reopenLog();
			else if (name == atomDiscardObjects)
				discardObjects(1, 0);
			else if (name == atomWriteoutObjects)
				writeoutObjects(1);
			else if (name == atomFreeChunkArenas)
				free_chunk_arenas();
			else {
				abortObject(object, 400,
					    internAtomF("Unknown action %s",
							name->string));
				releaseAtom(name);
				destroyAtomList(list);
				goto out;
			}
			releaseAtom(name);
		}
		destroyAtomList(list);
		object->date = current_time.tv_sec;
		object->age = current_time.tv_sec;
		object->headers = internAtom("\r\nLocation: /s_server/status?");
		object->code = 303;
		object->message = internAtom("Done");
		object->flags &= ~OBJECT_INITIAL;
		object->length = 0;
	} else {
		abortObject(object, 405, internAtom("Method not allowed"));
	}

      out:
	releaseAtom(data);
	notifyObject(object);
	requestor->connection->flags &= ~CONN_READER;
	return 1;
}
Пример #13
0
int
httpSpecialRequest(ObjectPtr object, int method, int from, int to,
		   HTTPRequestPtr requestor, void *closure)
{
	char buffer[1024];
	int hlen;

	if (method >= METHOD_POST) {
		return httpSpecialSideRequest(object, method, from, to,
					      requestor, closure);
	}

	if (!(object->flags & OBJECT_INITIAL)) {
		privatiseObject(object, 0);
		supersedeObject(object);
		object->flags &= ~(OBJECT_VALIDATING | OBJECT_INPROGRESS);
		notifyObject(object);
		return 1;
	}

	hlen = snnprintf(buffer, 0, 1024,
			 "\r\nServer: s_server" "\r\nContent-Type: text/html");
	object->date = current_time.tv_sec;
	object->age = current_time.tv_sec;
	object->headers = internAtomN(buffer, hlen);
	object->code = 200;
	object->message = internAtom("Okay");
	object->flags &= ~OBJECT_INITIAL;
	object->flags |= OBJECT_DYNAMIC;

	if (object->key_size == 8 && memcmp(object->key, "/s_server/", 8) == 0) {
		objectPrintf(object, 0,
			     "<!DOCTYPE HTML PUBLIC "
			     "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
			     "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
			     "<html><head>\n"
			     "<title>Polipo</title>\n"
			     "</head><body>\n"
			     "<h1>Polipo</h1>\n"
			     "<p><a href=\"status?\">Status report</a>.</p>\n"
			     "<p><a href=\"config?\">Current configuration</a>.</p>\n"
			     "<p><a href=\"servers?\">Known servers</a>.</p>\n"
#ifndef NO_DISK_CACHE
			     "<p><a href=\"index?\">Disk cache index</a>.</p>\n"
#endif
			     "</body></html>\n");
		object->length = object->size;
	} else if (matchUrl("/s_server/status", object)) {
		objectPrintf(object, 0,
			     "<!DOCTYPE HTML PUBLIC "
			     "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
			     "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
			     "<html><head>\n"
			     "<title>Polipo status report</title>\n"
			     "</head><body>\n"
			     "<h1>Polipo proxy on %s:%d: status report</h1>\n"
			     "<p>The %s proxy on %s:%d is %s.</p>\n"
			     "<p>There are %d public and %d private objects "
			     "currently in memory using %d KB in %d chunks "
			     "(%d KB allocated).</p>\n"
			     "<p>There are %d atoms.</p>"
			     "<p><form method=POST action=\"/s_server/status?\">"
			     "<input type=submit name=\"init-forbidden\" "
			     "value=\"Read forbidden file\"></form>\n"
			     "<form method=POST action=\"/s_server/status?\">"
			     "<input type=submit name=\"writeout-objects\" "
			     "value=\"Write out in-memory cache\"></form>\n"
			     "<form method=POST action=\"/s_server/status?\">"
			     "<input type=submit name=\"discard-objects\" "
			     "value=\"Discard in-memory cache\"></form>\n"
			     "<form method=POST action=\"/s_server/status?\">"
			     "<input type=submit name=\"reopen-log\" "
			     "value=\"Reopen log file\"></form>\n"
			     "<form method=POST action=\"/s_server/status?\">"
			     "<input type=submit name=\"free-chunk-arenas\" "
			     "value=\"Free chunk arenas\"></form></p>\n"
			     "<p><a href=\"/s_server/\">back</a></p>"
			     "</body></html>\n",
			     proxyName->string, proxyPort,
			     cacheIsShared ? "shared" : "private",
			     proxyName->string, proxyPort,
			     proxyOffline ? "off line" :
			     (relaxTransparency ?
			      "on line (transparency relaxed)" :
			      "on line"),
			     publicObjectCount, privateObjectCount,
			     used_chunks * CHUNK_SIZE / 1024, used_chunks,
			     totalChunkArenaSize() / 1024, used_atoms);
		object->expires = current_time.tv_sec;
		object->length = object->size;
	} else if (matchUrl("/s_server/config", object)) {
		fillSpecialObject(object, printConfig, NULL);
		object->expires = current_time.tv_sec + 5;
#ifndef NO_DISK_CACHE
	} else if (matchUrl("/s_server/index", object)) {
		int len;
		char *root;
		if (disableIndexing) {
			abortObject(object, 403,
				    internAtom("Action not allowed"));
			notifyObject(object);
			return 1;
		}
		len = MAX(0, object->key_size - 14);
		root = strdup_n((char *)object->key + 14, len);
		if (root == NULL) {
			abortObject(object, 503,
				    internAtom("Couldn't allocate root"));
			notifyObject(object);
			return 1;
		}
		writeoutObjects(1);
		fillSpecialObject(object, plainIndexDiskObjects, root);
		free(root);
		object->expires = current_time.tv_sec + 5;
	} else if (matchUrl("/s_server/recursive-index", object)) {
		int len;
		char *root;
		if (disableIndexing) {
			abortObject(object, 403,
				    internAtom("Action not allowed"));
			notifyObject(object);
			return 1;
		}
		len = MAX(0, object->key_size - 24);
		root = strdup_n((char *)object->key + 24, len);
		if (root == NULL) {
			abortObject(object, 503,
				    internAtom("Couldn't allocate root"));
			notifyObject(object);
			return 1;
		}
		writeoutObjects(1);
		fillSpecialObject(object, recursiveIndexDiskObjects, root);
		free(root);
		object->expires = current_time.tv_sec + 20;
#endif
	} else if (matchUrl("/s_server/servers", object)) {
		if (disableServersList) {
			abortObject(object, 403,
				    internAtom("Action not allowed"));
			notifyObject(object);
			return 1;
		}
		fillSpecialObject(object, serversList, NULL);
		object->expires = current_time.tv_sec + 2;
	} else {
		abortObject(object, 404, internAtom("Not found"));
	}

	object->flags &= ~OBJECT_VALIDATING;
	notifyObject(object);
	return 1;
}
Пример #14
0
int
parseConfigLine(char *line, char *filename, int lineno, int set)
{
    int x0, x1;
    int i, from, to;
    AtomPtr name, value;
    ConfigVariablePtr var;
    int iv;
    float fv;
    AtomPtr av;
    AtomListPtr alv;
    IntListPtr ilv;

    i = skipWhitespace(line, 0);
    if(line[i] == '\n' || line[i] == '\0' || line[i] == '#')
        return 0;

    x0 = i;
    while(letter(line[i]) || digit(line[i]))
        i++;
    x1 = i;

    i = skipWhitespace(line, i);
    if(line[i] != '=') {
        goto syntax;
    }
    i++;
    i = skipWhitespace(line, i);

    name = internAtomN(line + x0, x1 - x0);
    var = findConfigVariable(name);
    releaseAtom(name);

    if(set && var->setter == NULL)
        return -2;
 
    if(var == NULL) {
        if(!set) {
            do_log(L_ERROR, "%s:%d: unknown config variable ",
                   filename, lineno);
            do_log_n(L_ERROR, line + x0, x1 - x0);
            do_log(L_ERROR, "\n");
        }
        return -1;
    }
    
    i = skipWhitespace(line, i);
    switch(var->type) {
    case CONFIG_INT: case CONFIG_OCTAL: case CONFIG_HEX:
        i = parseInt(line, i, &iv);
        if(i < 0) goto syntax;
        if(set)
            var->setter(var, &iv);
        else
            *var->value.i = iv;
    break;
    case CONFIG_TIME:
        i = parseTime(line, i, &iv);
        if(i < 0) goto syntax;
        i = skipWhitespace(line, i);
        if(line[i] != '\n' && line[i] != '\0' && line[i] != '#')
            goto syntax;
        if(set)
            var->setter(var, &iv);
        else
            *var->value.i = iv;
        break;
    case CONFIG_BOOLEAN:
    case CONFIG_TRISTATE:
    case CONFIG_TETRASTATE:
    case CONFIG_PENTASTATE:
        iv = parseState(line, i, var->type);
        if(iv < 0)
            goto syntax;
        if(set)
            var->setter(var, &iv);
        else
            *var->value.i = iv;
        break;
    case CONFIG_FLOAT: 
        if(!digit(line[i]) && line[i] != '.')
            goto syntax;
        fv = atof(line + i);
        if(set)
            var->setter(var, &fv);
        else
            *var->value.f = fv;
        break;
    case CONFIG_ATOM: case CONFIG_ATOM_LOWER: case CONFIG_PASSWORD:
        i = parseAtom(line, i, &av, (var->type == CONFIG_ATOM_LOWER));
        if(i < 0) goto syntax;
        if(!av) {
            if(!set)
                do_log(L_ERROR, "%s:%d: couldn't allocate atom.\n",
                       filename, lineno);
            return -1;
        }
        i = skipWhitespace(line, i);
        if(line[i] != '\n' && line[i] != '\0' && line[i] != '#') {
            releaseAtom(av);
            goto syntax;
        }
        if(set)
            var->setter(var, &av);
        else {
            if(*var->value.a) releaseAtom(*var->value.a);
            *var->value.a = av;
        }
        break;
    case CONFIG_INT_LIST:
        ilv = makeIntList(0);
        if(ilv == NULL) {
            if(!set)
                do_log(L_ERROR, "%s:%d: couldn't allocate int list.\n",
                       filename, lineno);
            return -1;
        }
        while(1) {
            i = parseInt(line, i, &from);
            if(i < 0) goto syntax;
            to = from;
            i = skipWhitespace(line, i);
            if(line[i] == '-') {
                i = skipWhitespace(line, i + 1);
                i = parseInt(line, i, &to);
                if(i < 0) {
                    destroyIntList(ilv);
                    goto syntax;
                }
                i = skipWhitespace(line, i);
            }
            intListCons(from, to, ilv);
            if(line[i] == '\n' || line[i] == '\0' || line[i] == '#')
                break;
            if(line[i] != ',') {
                destroyIntList(ilv);
                goto syntax;
            }
            i = skipWhitespace(line, i + 1);
        }
        if(set)
            var->setter(var, &ilv);
        else {
            if(*var->value.il) destroyIntList(*var->value.il);
            *var->value.il = ilv;
        }
        break;
    case CONFIG_ATOM_LIST: case CONFIG_ATOM_LIST_LOWER:
        alv = makeAtomList(NULL, 0);
        if(alv == NULL) {
            if(!set)
                do_log(L_ERROR, "%s:%d: couldn't allocate atom list.\n",
                       filename, lineno);
            return -1;
        }
        while(1) {
            i = parseAtom(line, i, &value, 
                          (var->type == CONFIG_ATOM_LIST_LOWER));
            if(i < 0) goto syntax;
            if(!value) {
                if(!set)
                    do_log(L_ERROR, "%s:%d: couldn't allocate atom.\n",
                           filename, lineno);
                return -1;
            }
            atomListCons(value, alv);
            i = skipWhitespace(line, i);
            if(line[i] == '\n' || line[i] == '\0' || line[i] == '#')
                break;
            if(line[i] != ',') {
                destroyAtomList(alv);
                goto syntax;
            }
            i = skipWhitespace(line, i + 1);
        }
        if(set)
            var->setter(var, &alv);
        else {
            if(*var->value.al) destroyAtomList(*var->value.al);
            *var->value.al = alv;
        }
        break;
    default: abort();
    }
    return 1;

 syntax:
    if(!set)
        do_log(L_ERROR, "%s:%d: parse error.\n", filename, lineno);
    return -1;
}
Пример #15
0
AtomPtr
internAtom(const char *string)
{
    return internAtomN(string, strlen(string));
}
Пример #16
0
int insertParentProxy(char *buf,int i)  //humeng add 11.5.10
{
    ParentProxyPtr node,prtPrxs;
    AtomPtr temp,addr,key,atom, port,allowIP;
    AtomListPtr tempList;
    int x,y,rc;
    if(!buf[i]=='=') return(0);
    i++;
    skipBlank(buf,i);
    x=i;
    while(!(buf[i] == '\n' || buf[i] == '\0' || buf[i] == '#'))
    {
        skipBlank(buf,i);
        i++;
    }
    if(i>x+1)
    temp=internAtomN(buf+x,i-x-1);
    rc = atomSplit(temp, ':', &addr, &temp);
    if(rc <= 0) {
        do_log(L_ERROR, "Couldn't parse parentProxy.");
        return -1;
    }
    node=malloc(sizeof(ParentProxyRec));
    node->addr=addr;
    rc = atomSplit(temp, ':', &port, &temp);
    if(rc <= 0) {
        do_log(L_ERROR, "Couldn't parse parentProxy.");
        return -1;
    }
    node->port=port;
    //node->friendName=intern;
    rc = atomSplit(temp, '-', &key, &allowIP);
    if(rc <= 0) {
        do_log(L_ERROR, "Couldn't parse parentProxy.");
        return -1;
    }
    node->key=key;
    node->keyHash=keyHasher(key->string, key->length);
    tempList=findProxyClient(allowIP->string);
    if(tempList){
        node->allowIP=parseNetAddress(tempList);
    }
    if(ParentProxys==NULL)
    {
        ParentProxys=node;
        //updateParentProxy("59.66.24.76","123","166.111.132.138");
        return 1;
    }else
    {
        prtPrxs=ParentProxys;
        while(prtPrxs)
        {
             if(strcmp(prtPrxs->addr->string,node->addr->string)==0)
             {
                  prtPrxs->port=node->port;
                  prtPrxs->key=node->key;
                  prtPrxs->keyHash=node->keyHash;
                  prtPrxs->allowIP=node->allowIP;
                  return 1;
             }
             if(prtPrxs->next==NULL)
             {
                 prtPrxs->next=node;
                 return 1;
             }
             prtPrxs=prtPrxs->next;
        }
    }
        return 1;

}
Пример #17
0
static int
really_do_dns(AtomPtr name, ObjectPtr object)
{
    int rc;
    DnsQueryPtr query;
    AtomPtr message = NULL;
    int id;
    AtomPtr a = NULL;

    if(a == NULL) {
        if(name == atomLocalhost || name == atomLocalhostDot) {
            char s[1 + sizeof(HostAddressRec)];
            memset(s, 0, sizeof(s));
            s[0] = DNS_A;
            s[1] = 4;
            s[2] = 127;
            s[3] = 0;
            s[4] = 0;
            s[5] = 1;
            a = internAtomN(s, 1 + sizeof(HostAddressRec));
            if(a == NULL) {
                abortObject(object, 501,
                            internAtom("Couldn't allocate address"));
                notifyObject(object);
                errno = ENOMEM;
                return -1;
            }
        }
    }

    if(a == NULL) {
        struct in_addr ina;
        rc = inet_aton(name->string, &ina);
        if(rc == 1) {
            char s[1 + sizeof(HostAddressRec)];
            memset(s, 0, sizeof(s));
            s[0] = DNS_A;
            s[1] = 4;
            memcpy(s + 2, &ina, 4);
            a = internAtomN(s, 1 + sizeof(HostAddressRec));
            if(a == NULL) {
                abortObject(object, 501,
                            internAtom("Couldn't allocate address"));
                notifyObject(object);
                errno = ENOMEM;
                return -1;
            }
        }
    }
#ifdef HAVE_IPv6
    if(a == NULL)
        a = rfc2732(name);
#endif

    if(a) {
        object->headers = a;
        object->age = current_time.tv_sec;
        object->expires = current_time.tv_sec + 240;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
        notifyObject(object);
        return 0;
    }

    rc = establishDnsSocket();
    if(rc < 0) {
        do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
        message = internAtomError(-rc, "Couldn't establish DNS socket");
        goto fallback;
    }

    /* The id is used to speed up detecting replies to queries that
       are no longer current -- see dnsReplyHandler. */
    id = (idSeed++) & 0xFFFF;

    query = malloc(sizeof(DnsQueryRec));
    if(query == NULL) {
        do_log(L_ERROR, "Couldn't allocate DNS query.\n");
        message = internAtom("Couldn't allocate DNS query");
        goto fallback;
    }
    query->id = id;
    query->inet4 = NULL;
    query->inet6 = NULL;
    query->name = name;
    query->time = current_time.tv_sec;
    query->object = retainObject(object);
    query->timeout = 4;
    query->timeout_handler = NULL;
    query->next = NULL;

    query->timeout_handler = 
        scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
                          sizeof(query), &query);
    if(query->timeout_handler == NULL) {
        do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
        message = internAtom("Couldn't schedule DNS timeout handler");
        goto free_fallback;
    }
    insertQuery(query);

    object->flags |= OBJECT_INPROGRESS;
    rc = sendQuery(query);
    if(rc < 0) {
        if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
            object->flags &= ~OBJECT_INPROGRESS;
            message = internAtomError(-rc, "Couldn't send DNS query");
            goto remove_fallback;
        }
        /* else let it timeout */
    }
    releaseAtom(message);
    return 1;

 remove_fallback:
    removeQuery(query);
 free_fallback:
    releaseObject(query->object);
    cancelTimeEvent(query->timeout_handler);
    free(query);
 fallback:
    if(dnsUseGethostbyname >= 1) {
        releaseAtom(message);
        do_log(L_WARN, "Falling back on gethostbyname.\n");
        return really_do_gethostbyname(name, object);
    } else {
        abortObject(object, 501, message);
        notifyObject(object);
        return 1;
    }
}
Пример #18
0
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
    struct hostent *host;
    char *s;
    AtomPtr a;
    int i, j;
    int error;

    host = gethostbyname(name->string);
    if(host == NULL) {
        switch(h_errno) {
        case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
        case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
        case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
        case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
        default: error = EUNKNOWN; break;
        }
        if(error == EDNS_HOST_NOT_FOUND) {
            object->headers = NULL;
            object->age = current_time.tv_sec;
            object->expires = current_time.tv_sec + dnsNegativeTtl;
            object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
            object->flags &= ~OBJECT_INPROGRESS;
            notifyObject(object);
            return 0;
        } else {
            do_log_error(L_ERROR, error, "Gethostbyname failed");
            abortObject(object, 404, 
                        internAtomError(error, "Gethostbyname failed"));
            object->flags &= ~OBJECT_INPROGRESS;
            notifyObject(object);
            return 0;
        }
    }
    if(host->h_addrtype != AF_INET) {
        do_log(L_ERROR, "Address is not AF_INET.\n");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404, internAtom("Address is not AF_INET"));
        notifyObject(object);
        return -1;
    }
    if(host->h_length != sizeof(struct in_addr)) {
        do_log(L_ERROR, "Address size inconsistent.\n");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404, internAtom("Address size inconsistent"));
        notifyObject(object);
        return 0;
    }
    i = 0;
    while(host->h_addr_list[i] != NULL) i++;
    s = malloc(1 + i * sizeof(HostAddressRec));
    if(s == NULL) {
        a = NULL;
    } else {
        memset(s, 0, 1 + i * sizeof(HostAddressRec));
        s[0] = DNS_A;
        for(j = 0; j < i; j++) {
            s[j * sizeof(HostAddressRec) + 1] = 4;
            memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
                   sizeof(struct in_addr));
        }
        a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
        free(s);
    }
    if(!a) {
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 501, internAtom("Couldn't allocate address"));
        notifyObject(object);
        return 0;
    }
    object->headers = a;
    object->age = current_time.tv_sec;
    object->expires = current_time.tv_sec + dnsGethostbynameTtl;
    object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
    notifyObject(object);
    return 0;

}
Пример #19
0
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
    struct addrinfo *ai, *entry, hints;
    int rc;
    int error, i;
    char buf[1024];
    AtomPtr a;

    a = rfc2732(name);
    if(a) {
        object->headers = a;
        object->age = current_time.tv_sec;
        object->expires = current_time.tv_sec + 240;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
        notifyObject(object);
        return 0;
    }

    memset(&hints, 0, sizeof(hints));
    hints.ai_protocol = IPPROTO_TCP;
    if(dnsQueryIPv6 <= 0)
        hints.ai_family = AF_INET;
    else if(dnsQueryIPv6 >= 3)
        hints.ai_family = AF_INET6;

    rc = getaddrinfo(name->string, NULL, &hints, &ai);

    switch(rc) {
    case 0: error = 0; break;
    case EAI_FAMILY:
#ifdef EAI_ADDRFAMILY
    case EAI_ADDRFAMILY:
#endif
    case EAI_SOCKTYPE:
        error = EAFNOSUPPORT; break;
    case EAI_BADFLAGS: error = EINVAL; break;
    case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
#ifdef EAI_NONAME
    case EAI_NONAME:
#endif
#ifdef EAI_NODATA
    case EAI_NODATA:
#endif
        error = EDNS_NO_ADDRESS; break;
    case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
    case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
#ifdef EAI_MEMORY
    case EAI_MEMORY: error = ENOMEM; break;
#endif
    case EAI_SYSTEM: error = errno; break;
    default: error = EUNKNOWN;
    }

    if(error == EDNS_NO_ADDRESS) {
        object->headers = NULL;
        object->age = current_time.tv_sec;
        object->expires = current_time.tv_sec + dnsNegativeTtl;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
        notifyObject(object);
        return 0;
    } else if(error) {
        do_log_error(L_ERROR, error, "Getaddrinfo failed");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404,
                    internAtomError(error, "Getaddrinfo failed"));
        notifyObject(object);
        return 0;
    }

    entry = ai;
    buf[0] = DNS_A;
    i = 0;
    while(entry) {
        HostAddressRec host;
        int host_valid = 0;
        if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
            if(dnsQueryIPv6 < 3) {
                host.af = 4;
                memset(host.data, 0, sizeof(host.data));
                memcpy(&host.data,
                       &((struct sockaddr_in*)entry->ai_addr)->sin_addr, 
                       4);
                host_valid = 1;
            }
        } else if(entry->ai_family == AF_INET6 && 
                  entry->ai_protocol == IPPROTO_TCP) {
            if(dnsQueryIPv6 > 0) {
                host.af = 6;
                memset(&host.data, 0, sizeof(host.data));
                memcpy(&host.data,
                       &((struct sockaddr_in6*)entry->ai_addr)->sin6_addr, 
                       16);
                host_valid = 1;
            }
        }
        if(host_valid) {
            if(i >= 1024 / sizeof(HostAddressRec) - 2) {
                do_log(L_ERROR, "Too many addresses for host %s\n", 
                       name->string);
                break;
            }
            memcpy(buf + 1 + i * sizeof(HostAddressRec), 
                   &host, sizeof(HostAddressRec));
            i++;
        }
        entry = entry->ai_next;
    }
    freeaddrinfo(ai);
    if(i == 0) {
        do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404,
                    internAtom("Getaddrinfo returned no useful addresses"));
        notifyObject(object);
        return 0;
    }

    if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
        qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);

    a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
    if(a == NULL) {
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 501, internAtom("Couldn't allocate address"));
        notifyObject(object);
        return 0;
    }
    object->headers = a;
    object->age = current_time.tv_sec;
    object->expires = current_time.tv_sec + dnsGethostbynameTtl;
    object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
    notifyObject(object);
    return 0;
}
Пример #20
0
static int
dnsReplyHandler(int abort, FdEventHandlerPtr event)
{
    int fd = event->fd;
    char buf[2048];
    int len, rc;
    ObjectPtr object;
    unsigned ttl = 0;
    AtomPtr name, value, message = NULL;
    int id;
    int af;
    DnsQueryPtr query;
    AtomPtr cname = NULL;

    if(abort) {
        dnsSocketHandler = NULL;
        rc = establishDnsSocket();
        if(rc < 0) {
            do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
            /* At this point, we should abort all in-flight
               DNS requests.  Oh, well, they'll timeout anyway. */
        }
        return 1;
    }

    len = recv(fd, buf, 2048, 0);
    if(len <= 0) {
        if(errno == EINTR || errno == EAGAIN) return 0;
        /* This is where we get ECONNREFUSED for an ICMP port unreachable */
        do_log_error(L_ERROR, errno, "DNS: recv failed");
        dnsGethostbynameFallback(-1, message);
        return 0;
    }

    /* This could be a late reply to a query that timed out and was
       resent, a reply to a query that timed out, or a reply to an
       AAAA query when we already got a CNAME reply to the associated
       A.  We filter such replies straight away, without trying to
       parse them. */
    rc = dnsReplyId(buf, 0, len, &id);
    if(rc < 0) {
        do_log(L_WARN, "Short DNS reply.\n");
        return 0;
    }
    if(!findQuery(id, NULL)) {
        return 0;
    }

    rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
    if(rc < 0) {
        assert(value == NULL);
        /* We only want to fallback on gethostbyname if we received a
           reply that we could not understand.  What about truncated
           replies? */
        if(rc < 0) {
            do_log_error(L_WARN, -rc, "DNS");
            if(dnsUseGethostbyname >= 2 ||
               (dnsUseGethostbyname && 
                (rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
                 rc != -EDNS_FORMAT))) {
                dnsGethostbynameFallback(id, message);
                return 0;
            } else {
                message = internAtom(pstrerror(-rc));
            }
        } else {
            assert(name != NULL && id >= 0 && af >= 0);
        }
    }

    query = findQuery(id, name);
    if(query == NULL) {
        /* Duplicate id ? */
        releaseAtom(value);
        releaseAtom(name);
        return 0;
    }

    /* We're going to use the information in this reply.  If it was an
       error, construct an empty atom to distinguish it from information
       we're still waiting for. */
    if(value == NULL)
        value = internAtom("");

 again:
    if(af == 4) {
        if(query->inet4 == NULL) {
            query->inet4 = value;
            query->ttl4 = current_time.tv_sec + ttl;
        } else
            releaseAtom(value);
    } else if(af == 6) {
        if(query->inet6 == NULL) {
            query->inet6 = value;
            query->ttl6 = current_time.tv_sec + ttl;
        } else
            releaseAtom(value);
    } else if(af == 0) {
        /* Ignore errors in this case. */
        if(query->inet4 && query->inet4->length == 0) {
            releaseAtom(query->inet4);
            query->inet4 = NULL;
        }
        if(query->inet6 && query->inet6->length == 0) {
            releaseAtom(query->inet6);
            query->inet6 = NULL;
        }
        if(query->inet4 || query->inet6) {
            do_log(L_WARN, "Host %s has both %s and CNAME -- "
                   "ignoring CNAME.\n", scrub(query->name->string),
                   query->inet4 ? "A" : "AAAA");
            releaseAtom(value);
            value = internAtom("");
            af = query->inet4 ? 4 : 6;
            goto again;
        } else {
            cname = value;
        }
    }

    if(rc >= 0 && !cname &&
       ((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
        (dnsQueryIPv6 > 0 && query->inet6 == NULL)))
        return 0;

    /* This query is complete */

    cancelTimeEvent(query->timeout_handler);
    object = query->object;

    if(object->flags & OBJECT_INITIAL) {
        assert(!object->headers);
        if(cname) {
            assert(query->inet4 == NULL && query->inet6 == NULL);
            object->headers = cname;
            object->expires = current_time.tv_sec + ttl;
        } else if((!query->inet4 || query->inet4->length == 0) &&
                  (!query->inet6 || query->inet6->length == 0)) {
            releaseAtom(query->inet4);
            releaseAtom(query->inet6);
            object->expires = current_time.tv_sec + dnsNegativeTtl;
            abortObject(object, 500, retainAtom(message));
        } else if(!query->inet4 || query->inet4->length == 0) {
            object->headers = query->inet6;
            object->expires = query->ttl6;
            releaseAtom(query->inet4);
        } else if(!query->inet6 || query->inet6->length == 0) {
            object->headers = query->inet4;
            object->expires = query->ttl4;
            releaseAtom(query->inet6);
        } else {
            /* need to merge results */
            char buf[1024];
            if(query->inet4->length + query->inet6->length > 1024) {
                releaseAtom(query->inet4);
                releaseAtom(query->inet6);
                abortObject(object, 500, internAtom("DNS reply too long"));
            } else {
                if(dnsQueryIPv6 <= 1) {
                    memcpy(buf, query->inet4->string, query->inet4->length);
                    memcpy(buf + query->inet4->length,
                           query->inet6->string + 1, query->inet6->length - 1);
                } else {
                    memcpy(buf, query->inet6->string, query->inet6->length);
                    memcpy(buf + query->inet6->length,
                           query->inet4->string + 1, query->inet4->length - 1);
                }
                object->headers =
                    internAtomN(buf, 
                                query->inet4->length + 
                                query->inet6->length - 1);
                if(object->headers == NULL)
                    abortObject(object, 500, 
                                internAtom("Couldn't allocate DNS atom"));
            }
            object->expires = MIN(query->ttl4, query->ttl6);
        }
        object->age = current_time.tv_sec;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
    } else {
        do_log(L_WARN, "DNS object ex nihilo for %s.\n",
               scrub(query->name->string));
    }
    
    removeQuery(query);
    free(query);

    releaseAtom(name);
    releaseAtom(message);
    releaseNotifyObject(object);
    return 0;
}
Пример #21
0
static int
dnsDecodeReply(char *buf, int offset, int n, int *id_return,
               AtomPtr *name_return, AtomPtr *value_return,
               int *af_return, unsigned *ttl_return)
{
    int i = offset, j, m;
    int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength;
    int class, type;
    unsigned int ttl;
    char b[2048];
    int af = -1;
    AtomPtr name = NULL, value;
    char addresses[1024];
    int addr_index = 0;
    int error = EDNS_NO_ADDRESS;
    unsigned final_ttl = 7 * 24 * 3600;
    int dnserror;

    if(n - i < 12) {
        error = EDNS_INVALID;
        goto fail;
    }

    DO_NTOHS(id, &buf[i]); i += 2;
    DO_NTOHS(b23, &buf[i]); i += 2;
    DO_NTOHS(qdcount, &buf[i]); i += 2;
    DO_NTOHS(ancount, &buf[i]); i += 2;
    DO_NTOHS(nscount, &buf[i]); i += 2;
    DO_NTOHS(arcount, &buf[i]); i += 2;

    do_log(D_DNS, 
           "DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
           "nscount %d, arcount %d\n",
           id, b23, qdcount, ancount, nscount, arcount);

    if((b23 & (0xF870)) != 0x8000) {
        do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23);
        error = EDNS_INVALID;
        goto fail;
    }

    dnserror = b23 & 0xF;

    if(b23 & 0x200) {
        do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23);
    }

    if(dnserror || qdcount != 1) {
        if(!dnserror)
            do_log(L_ERROR, 
                   "Unexpected number %d of DNS questions.\n", qdcount);
        if(dnserror == 1)
            error = EDNS_FORMAT;
        else if(dnserror == 2)
            error = EDNS_NO_RECOVERY;
        else if(dnserror == 3)
            error = EDNS_HOST_NOT_FOUND;
        else if(dnserror == 4 || dnserror == 5)
            error = EDNS_REFUSED;
        else if(dnserror == 0)
            error = EDNS_INVALID;
        else
            error = EUNKNOWN;
        goto fail;
    }

    /* We do this early, so that we can return the address family to
       the caller in case of error. */
    i = labelsToString(buf, i, n, b, 2048, &m);
    if(i < 0) {
        error = EDNS_FORMAT;
        goto fail;
    }
    DO_NTOHS(type, &buf[i]); i += 2;
    DO_NTOHS(class, &buf[i]); i += 2;

    if(type == 1)
        af = 4;
    else if(type == 28)
        af = 6;
    else {
        error = EDNS_FORMAT;
        goto fail;
    }

    do_log(D_DNS, "DNS q: ");
    do_log_n(D_DNS, b, m);
    do_log(D_DNS, " (%d, %d)\n", type, class);
    name = internAtomLowerN(b, m);
    if(name == NULL) {
        error = ENOMEM;
        goto fail;
    }

    if(class != 1) {
        error = EDNS_FORMAT;
        goto fail;
    }

#define PARSE_ANSWER(kind, label) \
do { \
    i = labelsToString(buf, i, 1024, b, 2048, &m); \
    if(i < 0) goto label; \
    DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
    DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
    DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
    DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
    do_log(D_DNS, "DNS " kind ": "); \
    do_log_n(D_DNS, b, m); \
    do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
           type, class, rdlength, ttl); \
   } while(0)


    for(j = 0; j < ancount; j++) {
        PARSE_ANSWER("an", fail);
        if(strcasecmp_n(name->string, b, m) == 0) {
            if(class != 1) {
                do_log(D_DNS, "DNS: %s: unknown class %d.\n", 
                       name->string, class);
                error = EDNS_UNSUPPORTED;
                goto cont;
            }
            if(type == 1 || type == 28) {
                if((type == 1 && rdlength != 4) ||
                   (type == 28 && rdlength != 16)) {
                    do_log(L_ERROR, 
                           "DNS: %s: unexpected length %d of %s record.\n",
                           scrub(name->string),
                           rdlength, type == 1 ? "A" : "AAAA");
                    error = EDNS_INVALID;
                    if(rdlength <= 0 || rdlength >= 32)
                        goto fail;
                    goto cont;
                }
                if(af == 0) {
                    do_log(L_WARN, "DNS: %s: host has both A and CNAME -- "
                           "ignoring CNAME.\n", scrub(name->string));
                    addr_index = 0;
                    af = -1;
                }
                if(type == 1) {
                    if(af < 0)
                        af = 4;
                    else if(af == 6) {
                        do_log(L_WARN, "Unexpected AAAA reply.\n");
                        goto cont;
                    }
                } else {
                    if(af < 0)
                        af = 6;
                    else if(af == 4) {
                        do_log(L_WARN, "Unexpected A reply.\n");
                        goto cont;
                    }
                }

                if(addr_index == 0) {
                    addresses[0] = DNS_A;
                    addr_index++;
                } else {
                    if(addr_index > 1000) {
                        error = EDNS_INVALID;
                        goto fail;
                    }
                }
                assert(addresses[0] == DNS_A);
                if(final_ttl > ttl)
                    final_ttl = ttl;
                memset(&addresses[addr_index], 0, sizeof(HostAddressRec));
                if(type == 1) {
                    addresses[addr_index] = 4;
                    memcpy(addresses + addr_index + 1, buf + i, 4);
                } else {
                    addresses[addr_index] = 6;
                    memcpy(addresses + addr_index + 1, buf + i, 16);
                }
                addr_index += sizeof(HostAddressRec);
            } else if(type == 5) {
                int j, k;
                if(af != 0 && addr_index > 0) {
                    do_log(L_WARN, "DNS: host has both CNAME and A -- "
                           "ignoring CNAME.\n");
                    goto cont;
                }
                af = 0;

                if(addr_index != 0) {
                    /* Only warn if the CNAMEs are not identical */
                    char tmp[512]; int jj, kk;
                    assert(addresses[0] == DNS_CNAME);
                    jj = labelsToString(buf, i, n,
                                        tmp, 512, &kk);
                    if(jj < 0 ||
                       kk != strlen(addresses + 1) ||
                       memcmp(addresses + 1, tmp, kk) != 0) {
                        do_log(L_WARN, "DNS: "
                               "%s: host has multiple CNAMEs -- "
                               "ignoring subsequent.\n",
                               scrub(name->string));

                    }
                    goto cont;
                }
                addresses[0] = DNS_CNAME;
                addr_index++;
                j = labelsToString(buf, i, n,
                                   addresses + 1, 1020, &k);
                if(j < 0) {
                    addr_index = 0;
                    error = ENAMETOOLONG;
                    continue;
                }
                addr_index = k + 1;
            } else {
                error = EDNS_NO_ADDRESS;
                i += rdlength;
                continue;
            }

        }
    cont:
        i += rdlength;
    }

#if (LOGGING_MAX & D_DNS)
    for(j = 0; j < nscount; j++) {
        PARSE_ANSWER("ns", nofail);
        i += rdlength;
    }

    for(j = 0; j < arcount; j++) {
        PARSE_ANSWER("ar", nofail);
        i += rdlength;
    }

 nofail:
#endif

#undef PARSE_ANSWER

    do_log(D_DNS, "DNS: %d bytes\n", addr_index);
    if(af < 0)
        goto fail;

    value = internAtomN(addresses, addr_index);
    if(value == NULL) {
        error = ENOMEM;
        goto fail;
    }

    assert(af >= 0);
    *id_return = id;
    *name_return = name;
    *value_return = value;
    *af_return = af;
    *ttl_return = final_ttl;
    return 1;

 fail:
    *id_return = id;
    *name_return = name;
    *value_return = NULL;
    *af_return = af;
    return -error;
}