/*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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
AtomPtr internAtom(const char *string) { return internAtomN(string, strlen(string)); }
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; }
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; } }
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; }
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; }
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; }
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; }