static void log_error( mcontext *c, const char *msg ) { do_log(c,msg,false); // add to apache log do_log(c,msg,true); // display to user }
int readDomainFile(char *filename) { FILE *in; char buf[512]; char *rs; int i, j, is_regex, start; in = fopen(filename, "r"); if(in == NULL) { if(errno != ENOENT) do_log_error(L_ERROR, errno, "Couldn't open file %s", filename); return -1; } while(1) { rs = fgets(buf, 512, in); if(rs == NULL) break; for(i = 0; i < 512; i++) { if(buf[i] != ' ' && buf[i] != '\t') break; } start = i; for(i = start; i < 512; i++) { if(buf[i] == '#' || buf[i] == '\r' || buf[i] == '\n') break; } while(i > start) { if(buf[i - 1] != ' ' && buf[i - 1] != '\t') break; i--; } if(i <= start) continue; /* The significant part of the line is now between start and i */ is_regex = 0; for(j = start; j < i; j++) { if(buf[j] == '\\' || buf[j] == '*' || buf[j] == '/') { is_regex = 1; break; } } if(is_regex) { while(rlen + i - start + 8 >= rsize) { char *new_regexbuf; new_regexbuf = realloc(regexbuf, rsize * 2 + 1); if(new_regexbuf == NULL) { do_log(L_ERROR, "Couldn't reallocate regex.\n"); fclose(in); return -1; } regexbuf = new_regexbuf; rsize = rsize * 2 + 1; } if(rlen != 0) rlen = snnprintf(regexbuf, rlen, rsize, "|"); rlen = snnprintf(regexbuf, rlen, rsize, "("); rlen = snnprint_n(regexbuf, rlen, rsize, buf + start, i - start); rlen = snnprintf(regexbuf, rlen, rsize, ")"); } else { DomainPtr new_domain; if(dlen >= dsize - 1) { DomainPtr *new_domains; new_domains = realloc(domains, (dsize * 2 + 1) * sizeof(DomainPtr)); if(new_domains == NULL) { do_log(L_ERROR, "Couldn't reallocate domain list.\n"); fclose(in); return -1; } domains = new_domains; dsize = dsize * 2 + 1; } new_domain = malloc(sizeof(DomainRec) - 1 + i - start); if(new_domain == NULL) { do_log(L_ERROR, "Couldn't allocate domain.\n"); fclose(in); return -1; } new_domain->length = i - start; memcpy(new_domain->domain, buf + start, i - start); domains[dlen++] = new_domain; } } fclose(in); return 1; }
/** Add new standard attributes, or change permissions on them. * \verbatim * Given the name and permission string for an attribute, add it to * the attribute table (or modify the permissions if it's already * there). Permissions may be changed retroactively, which modifies * permissions on any copies of that attribute set on objects in the * database. This is the top-level code for @attribute/access. * \endverbatim * \param player the enactor. * \param name the attribute name. * \param perms a string of attribute permissions, space-separated. * \param retroactive if true, apply the permissions retroactively. */ void do_attribute_access(dbref player, char *name, char *perms, int retroactive) { ATTR *ap, *ap2; privbits flags = 0; int i; int insert = 0; /* Parse name and perms */ if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } if (strcasecmp(perms, "none")) { flags = list_to_privs(attr_privs_set, perms, 0); if (!flags) { notify(player, T("I don't understand those permissions.")); return; } } upcasestr(name); /* Is this attribute already in the table? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (ap) { if (AF_Internal(ap)) { /* Don't muck with internal attributes */ notify(player, T("That attribute's permissions can not be changed.")); return; } /* Preserve any existing @attribute/limit */ flags |= (AL_FLAGS(ap) & (AF_RLIMIT | AF_ENUM)); } else { /* Create fresh if the name is ok */ if (!good_atr_name(name)) { notify(player, T("Invalid attribute name.")); return; } insert = 1; ap = (ATTR *) mush_malloc(sizeof(ATTR), "ATTR"); if (!ap) { notify(player, T("Critical memory failure - Alert God!")); do_log(LT_ERR, 0, 0, "do_attribute_access: unable to malloc ATTR"); return; } AL_NAME(ap) = strdup(name); ap->data = NULL_CHUNK_REFERENCE; } AL_FLAGS(ap) = flags; AL_CREATOR(ap) = player; /* Only insert when it's not already in the table */ if (insert) { ptab_insert_one(&ptab_attrib, name, ap); } /* Ok, now we need to see if there are any attributes of this name * set on objects in the db. If so, and if we're retroactive, set * perms/creator */ if (retroactive) { for (i = 0; i < db_top; i++) { if ((ap2 = atr_get_noparent(i, name))) { if (AL_FLAGS(ap2) & AF_ROOT) AL_FLAGS(ap2) = flags | AF_ROOT; else AL_FLAGS(ap2) = flags; AL_CREATOR(ap2) = player; } } } notify_format(player, T("%s -- Attribute permissions now: %s"), name, privs_to_string(attr_privs_view, flags)); }
int redirectorStreamHandler2(int status, FdEventHandlerPtr event, StreamRequestPtr srequest) { RedirectRequestPtr request = (RedirectRequestPtr)srequest->data; char *c, *c2, *buf; AtomPtr url = request->url; AtomPtr message = NULL; AtomPtr headers = NULL; int code = 0; if(status < 0) { do_log_error(L_ERROR, -status, "Read from redirector failed"); request->handler(status, request->url, NULL, NULL, request->data); goto kill; } c = memchr(redirector_buffer, '\n', srequest->offset); if(!c) { if(!status && srequest->offset < REDIRECTOR_BUFFER_SIZE) return 0; do_log(L_ERROR, "Redirector returned incomplete reply.\n"); request->handler(-EREDIRECTOR, request->url, NULL, NULL, request->data); goto kill; } c2 = memchr(redirector_buffer, ' ', srequest->offset); if (c2 != NULL) c = c2; *c = '\0'; buf = redirector_buffer; if (digit(buf[0]) && digit(buf[1]) && digit(buf[2]) && buf[3] == ':') { code = strtol(buf, NULL, 10); buf += 4; } if(c > buf + 1 && (c - buf != request->url->length || memcmp(buf, request->url->string, request->url->length) != 0)) { if (!redirectorIsServerSide || (300 <= code && code <= 399)) { if (!(300 <= code && code <= 399)) code = redirectorRedirectCode; message = internAtom("Redirected by external redirector"); if(message == NULL) { request->handler(-ENOMEM, request->url, NULL, NULL, request->data); goto kill; } headers = internAtomF("\r\nLocation: %s", redirector_buffer); if(headers == NULL) { releaseAtom(message); request->handler(-ENOMEM, request->url, NULL, NULL, request->data); goto kill; } } else { url = internAtom(buf); if(url == NULL) { request->handler(-ENOMEM, request->url, NULL, NULL, request->data); goto kill; } } } request->handler(code, url, message, headers, request->data); goto cont; cont: redirectorDestroyRequest(request); redirectorTrigger(); return 1; kill: redirectorKill(); goto cont; }
static int log_debug(lua_State *L) { return do_log(L, LOG_PRIORITY_DEBUG, false); }
static int parseResolvConf(char *filename) { FILE *f; char buf[512]; char *p, *q; int n; AtomPtr nameserver = NULL; f = fopen(filename, "r"); if(f == NULL) { do_log_error(L_ERROR, errno, "DNS: couldn't open %s", filename); return 0; } while(1) { p = fgets(buf, 512, f); if(p == NULL) break; n = strlen(buf); if(buf[n - 1] != '\n') { int c; do_log(L_WARN, "DNS: overly long line in %s -- skipping.\n", filename); do { c = fgetc(f); if(c == EOF) break; } while(c != '\n'); if(c == EOF) break; } while(*p == ' ' || *p == '\t') p++; if(strcasecmp_n("nameserver", p, 10) != 0) continue; p += 10; while(*p == ' ' || *p == '\t') p++; q = p; while(*q == '.' || *q == ':' || digit(*q) || letter(*q)) q++; if(*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') { do_log(L_WARN, "DNS: couldn't parse line in %s -- skipping.\n", filename); continue; } nameserver = internAtomLowerN(p, q - p); break; } fclose(f); if(nameserver) { dnsNameServer = nameserver; return 1; } else { return 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; #ifdef NO_ADDRESS case NO_ADDRESS: error = EDNS_NO_ADDRESS; break; #endif 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; }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { TunnelPtr tunnel; int port; char *p, *q; /* PSIPHON */ if(psiphonStats) { /* Update the page view stats by printf-ing the URI. Our stdout is piped to the client process. */ printf("PSIPHON-PAGE-VIEW-HTTPS:>>%s<<\n", url->string); fflush(NULL); } /* /PSIPHON */ tunnel = makeTunnel(fd, buf, offset, len); if(tunnel == NULL) { do_log(L_ERROR, "Couldn't allocate tunnel.\n"); releaseAtom(url); dispose_chunk(buf); CLOSE(fd); return; } if(proxyOffline) { do_log(L_INFO, "Attemted CONNECT when disconnected.\n"); releaseAtom(url); tunnelError(tunnel, 502, internAtom("Cannot CONNECT when disconnected.")); return; } p = memrchr(url->string, ':', url->length); q = NULL; if(p) port = strtol(p + 1, &q, 10); if(!p || q != url->string + url->length) { do_log(L_ERROR, "Couldn't parse CONNECT.\n"); releaseAtom(url); tunnelError(tunnel, 400, internAtom("Couldn't parse CONNECT")); return; } tunnel->hostname = internAtomLowerN(url->string, p - url->string); if(tunnel->hostname == NULL) { releaseAtom(url); tunnelError(tunnel, 501, internAtom("Couldn't allocate hostname")); return; } /* PSIPHON Checking if tunnel is allowed on a particular port is not needed if the proxy accepts connections made only from localhost */ /* if(!intListMember(port, tunnelAllowedPorts)) { releaseAtom(url); tunnelError(tunnel, 403, internAtom("Forbidden port")); return; } */ /* /PSIPHON */ tunnel->port = port; if (tunnelIsMatched(url->string, url->length, tunnel->hostname->string, tunnel->hostname->length)) { releaseAtom(url); tunnelError(tunnel, 404, internAtom("Forbidden tunnel")); logTunnel(tunnel,1); return; } logTunnel(tunnel,0); releaseAtom(url); /* PSIPHON split tunneling option*/ /* This was the original: if(socksParentProxy) do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); */ if(socksParentProxy) { if(splitTunneling) { do_gethostbyname_socks(parentHost ? parentHost->string : tunnel->hostname->string, 0, tunnelSplitTunnelingDnsHandler, tunnel); } else { do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); } } /* /PSIPHON */ else do_gethostbyname(parentHost ? parentHost->string : tunnel->hostname->string, 0, tunnelDnsHandler, tunnel); }
int do_gethostbyname(char *origname, int count, int (*handler)(int, GethostbynameRequestPtr), void *data) { ObjectPtr object; int n = strlen(origname); AtomPtr name; GethostbynameRequestRec request; int done, rc; memset(&request, 0, sizeof(request)); request.name = NULL; request.addr = NULL; request.error_message = NULL; request.count = count; request.handler = handler; request.data = data; if(n <= 0 || n > 131) { if(n <= 0) { request.error_message = internAtom("empty name"); do_log(L_ERROR, "Empty DNS name.\n"); done = handler(-EINVAL, &request); } else { request.error_message = internAtom("name too long"); do_log(L_ERROR, "DNS name too long.\n"); done = handler(-ENAMETOOLONG, &request); } assert(done); releaseAtom(request.error_message); return 1; } if(origname[n - 1] == '.') n--; name = internAtomLowerN(origname, n); if(name == NULL) { request.error_message = internAtom("couldn't allocate name"); do_log(L_ERROR, "Couldn't allocate DNS name.\n"); done = handler(-ENOMEM, &request); assert(done); releaseAtom(request.error_message); return 1; } request.name = name; request.addr = NULL; request.error_message = NULL; request.count = count; request.object = NULL; request.handler = handler; request.data = data; object = findObject(OBJECT_DNS, name->string, name->length); if(object == NULL || objectMustRevalidate(object, NULL)) { if(object) { privatiseObject(object, 0); releaseObject(object); } object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0, NULL, NULL); if(object == NULL) { request.error_message = internAtom("Couldn't allocate object"); do_log(L_ERROR, "Couldn't allocate DNS object.\n"); done = handler(-ENOMEM, &request); assert(done); releaseAtom(name); releaseAtom(request.error_message); return 1; } } if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) == OBJECT_INITIAL) { if(dnsUseGethostbyname >= 3) rc = really_do_gethostbyname(name, object); else rc = really_do_dns(name, object); if(rc < 0) { assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS))); goto fail; } } if(dnsUseGethostbyname >= 3) assert(!(object->flags & OBJECT_INITIAL)); #ifndef NO_FANCY_RESOLVER if(object->flags & OBJECT_INITIAL) { ConditionHandlerPtr chandler; assert(object->flags & OBJECT_INPROGRESS); request.object = object; chandler = conditionWait(&object->condition, dnsHandler, sizeof(request), &request); if(chandler == NULL) goto fail; return 1; } #endif if(object->headers && object->headers->length > 0) { if(object->headers->string[0] == DNS_A) assert(((object->headers->length - 1) % sizeof(HostAddressRec)) == 0); else assert(object->headers->string[0] == DNS_CNAME); request.addr = retainAtom(object->headers); } else if(object->message) { request.error_message = retainAtom(object->message); } releaseObject(object); if(request.addr && request.addr->length > 0) done = handler(1, &request); else done = handler(-EDNS_HOST_NOT_FOUND, &request); assert(done); releaseAtom(request.addr); request.addr = NULL; releaseAtom(request.name); request.name = NULL; releaseAtom(request.error_message); request.error_message = NULL; return 1; fail: releaseNotifyObject(object); done = handler(-errno, &request); assert(done); releaseAtom(name); return 1; }
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 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; }
static int dnsGethostbynameFallback(int id, AtomPtr message) { DnsQueryPtr query, previous; ObjectPtr object; if(inFlightDnsQueries == NULL) { releaseAtom(message); return 1; } query = NULL; if(id < 0 || inFlightDnsQueries->id == id) { previous = NULL; query = inFlightDnsQueries; } else { previous = inFlightDnsQueries; while(previous->next) { if(previous->next->id == id) { query = previous->next; break; } previous = previous->next; } if(!query) { previous = NULL; query = inFlightDnsQueries; } } if(previous == NULL) { inFlightDnsQueries = query->next; if(inFlightDnsQueries == NULL) inFlightDnsQueriesLast = NULL; } else { previous->next = query->next; if(query->next == NULL) inFlightDnsQueriesLast = NULL; } object = makeObject(OBJECT_DNS, query->name->string, query->name->length, 1, 0, NULL, NULL); if(!object) { do_log(L_ERROR, "Couldn't make DNS object.\n"); releaseAtom(query->name); releaseAtom(message); releaseObject(query->object); cancelTimeEvent(query->timeout_handler); free(query); return -1; } if(dnsUseGethostbyname >= 1) { releaseAtom(message); do_log(L_WARN, "Falling back to using system resolver.\n"); really_do_gethostbyname(retainAtom(query->name), object); } else { releaseAtom(object->message); object->message = message; object->flags &= ~OBJECT_INPROGRESS; releaseNotifyObject(object); } cancelTimeEvent(query->timeout_handler); releaseAtom(query->name); if(query->inet4) releaseAtom(query->inet4); if(query->inet6) releaseAtom(query->inet6); releaseObject(query->object); free(query); return 1; }
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; }
void parseDomainFile(AtomPtr file, DomainPtr **domains_return, regex_t **regex_return) { struct stat ss; regex_t *regex; int rc; if(*domains_return) { DomainPtr *domain = *domains_return; while(*domain) { free(*domain); domain++; } free(*domains_return); *domains_return = NULL; } if(*regex_return) { regfree(*regex_return); *regex_return = NULL; } if(!file || file->length == 0) return; domains = malloc(64 * sizeof(DomainPtr)); if(domains == NULL) { do_log(L_ERROR, "Couldn't allocate domain list.\n"); return; } dlen = 0; dsize = 64; regexbuf = malloc(512); if(regexbuf == NULL) { do_log(L_ERROR, "Couldn't allocate regex.\n"); free(domains); return; } rlen = 0; rsize = 512; rc = stat(file->string, &ss); if(rc < 0) { if(errno != ENOENT) do_log_error(L_WARN, errno, "Couldn't stat file %s", file->string); } else { if(!S_ISDIR(ss.st_mode)) readDomainFile(file->string); else { char *fts_argv[2]; FTS *fts; FTSENT *fe; fts_argv[0] = file->string; fts_argv[1] = NULL; fts = fts_open(fts_argv, FTS_LOGICAL, NULL); if(fts) { while(1) { fe = fts_read(fts); if(!fe) break; if(fe->fts_info != FTS_D && fe->fts_info != FTS_DP && fe->fts_info != FTS_DC && fe->fts_info != FTS_DNR) readDomainFile(fe->fts_accpath); } fts_close(fts); } else { do_log_error(L_ERROR, errno, "Couldn't scan directory %s", file->string); } } } if(dlen > 0) { domains[dlen] = NULL; } else { free(domains); domains = NULL; } if(rlen > 0) { regex = malloc(sizeof(regex_t)); rc = regcomp(regex, regexbuf, REG_EXTENDED | REG_NOSUB); if(rc != 0) { char errbuf[100]; regerror(rc, regex, errbuf, 100); do_log(L_ERROR, "Couldn't compile regex: %s.\n", errbuf); free(regex); regex = NULL; } } else { regex = NULL; } free(regexbuf); *domains_return = domains; *regex_return = regex; return; }
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; } }
int redirectorStreamHandler2(int status, FdEventHandlerPtr event, StreamRequestPtr srequest) { RedirectRequestPtr request = (RedirectRequestPtr)srequest->data; char *c; AtomPtr message; AtomPtr headers; int code; if(status < 0) { do_log_error(L_ERROR, -status, "Read from redirector failed"); request->handler(status, request->url, NULL, NULL, request->data); goto kill; } c = memchr(redirector_buffer, '\n', srequest->offset); if(!c) { if(!status && srequest->offset < REDIRECTOR_BUFFER_SIZE) return 0; do_log(L_ERROR, "Redirector returned incomplete reply.\n"); request->handler(-EREDIRECTOR, request->url, NULL, NULL, request->data); goto kill; } *c = '\0'; if(srequest->offset > c + 1 - redirector_buffer) do_log(L_WARN, "Stray bytes in redirector output.\n"); if(c > redirector_buffer + 1 && (c - redirector_buffer != request->url->length || memcmp(redirector_buffer, request->url->string, request->url->length) != 0)) { code = redirectorRedirectCode; message = internAtom("Redirected by external redirector"); if(message == NULL) { request->handler(-ENOMEM, request->url, NULL, NULL, request->data); goto kill; } headers = internAtomF("\r\nLocation: %s", redirector_buffer); if(headers == NULL) { releaseAtom(message); request->handler(-ENOMEM, request->url, NULL, NULL, request->data); goto kill; } } else { code = 0; message = NULL; headers = NULL; } request->handler(code, request->url, message, headers, request->data); goto cont; cont: redirectorDestroyRequest(request); redirectorTrigger(); return 1; kill: redirectorKill(); goto cont; }
int main(int argc, char **argv) { int sock; uid_t own_u; gid_t own_g; char *rpath = NULL; char *sock_name = NULL; struct stat stbuf; int c, option_index; #ifdef FS_IOC_GETVERSION int retval; struct statfs st_fs; #endif is_daemon = true; sock = -1; own_u = own_g = -1; while (1) { option_index = 0; c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts, &option_index); if (c == -1) { break; } switch (c) { case 'p': rpath = g_strdup(optarg); break; case 'n': is_daemon = false; break; case 'f': sock = atoi(optarg); break; case 's': sock_name = g_strdup(optarg); break; case 'u': own_u = atoi(optarg); break; case 'g': own_g = atoi(optarg); break; case '?': case 'h': default: usage(argv[0]); exit(EXIT_FAILURE); } } /* Parameter validation */ if ((sock_name == NULL && sock == -1) || rpath == NULL) { fprintf(stderr, "socket, socket descriptor or path not specified\n"); usage(argv[0]); return -1; } if (sock_name && sock != -1) { fprintf(stderr, "both named socket and socket descriptor specified\n"); usage(argv[0]); exit(EXIT_FAILURE); } if (sock_name && (own_u == -1 || own_g == -1)) { fprintf(stderr, "owner uid:gid not specified, "); fprintf(stderr, "owner uid:gid specifies who can access the socket file\n"); usage(argv[0]); exit(EXIT_FAILURE); } if (lstat(rpath, &stbuf) < 0) { fprintf(stderr, "invalid path \"%s\" specified, %s\n", rpath, strerror(errno)); exit(EXIT_FAILURE); } if (!S_ISDIR(stbuf.st_mode)) { fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); exit(EXIT_FAILURE); } if (is_daemon) { if (daemon(0, 0) < 0) { fprintf(stderr, "daemon call failed\n"); exit(EXIT_FAILURE); } openlog(PROGNAME, LOG_PID, LOG_DAEMON); } do_log(LOG_INFO, "Started\n"); if (sock_name) { sock = proxy_socket(sock_name, own_u, own_g); if (sock < 0) { goto error; } } if (chroot(rpath) < 0) { do_perror("chroot"); goto error; } if (chdir("/") < 0) { do_perror("chdir"); goto error; } get_version = false; #ifdef FS_IOC_GETVERSION /* check whether underlying FS support IOC_GETVERSION */ retval = statfs("/", &st_fs); if (!retval) { switch (st_fs.f_type) { case EXT2_SUPER_MAGIC: case BTRFS_SUPER_MAGIC: case REISERFS_SUPER_MAGIC: case XFS_SUPER_MAGIC: get_version = true; break; } } #endif umask(0); if (init_capabilities() < 0) { goto error; } process_requests(sock); error: do_log(LOG_INFO, "Done\n"); closelog(); return 0; }
static void logTunnel(TunnelPtr tunnel, int blocked) { do_log(L_TUNNEL,"tunnel %s:%d %s\n", tunnel->hostname->string, tunnel->port, blocked ? "blocked" : "allowed"); }
CP_HIDDEN void cpi_log(cp_context_t *context, cp_log_severity_t severity, const char *msg) { assert(context != NULL); assert(msg != NULL); assert(severity >= CP_LOG_DEBUG && severity <= CP_LOG_ERROR); do_log(context, severity, _(msg)); }
static int log_info(lua_State *L) { return do_log(L, LOG_PRIORITY_INFO, false); }
int specialRequestHandler(int status, FdEventHandlerPtr event, StreamRequestPtr srequest) { SpecialRequestPtr request = srequest->data; int rc; int killed = 0; if(status < 0) { kill(request->pid, SIGTERM); killed = 1; request->object->flags &= ~OBJECT_INPROGRESS; abortObject(request->object, 502, internAtomError(-status, "Couldn't read from client")); goto done; } if(srequest->offset > 0) { rc = objectAddData(request->object, request->buf, request->offset, srequest->offset); if(rc < 0) { kill(request->pid, SIGTERM); killed = 1; request->object->flags &= ~OBJECT_INPROGRESS; abortObject(request->object, 503, internAtom("Couldn't add data to connection")); goto done; } request->offset += srequest->offset; } if(status) { request->object->flags &= ~OBJECT_INPROGRESS; request->object->length = request->object->size; goto done; } /* If we're the only person interested in this object, let's abort it now. */ if(request->object->refcount <= 1) { kill(request->pid, SIGTERM); killed = 1; request->object->flags &= ~OBJECT_INPROGRESS; abortObject(request->object, 500, internAtom("Aborted")); goto done; } notifyObject(request->object); do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, CHUNK_SIZE, specialRequestHandler, request); return 1; done: close(request->fd); dispose_chunk(request->buf); releaseNotifyObject(request->object); /* That's a blocking wait. It shouldn't block for long, as we've either already killed the child, or else we got EOF from it. */ do { rc = waitpid(request->pid, &status, 0); } while(rc < 0 && errno == EINTR); if(rc < 0) { do_log(L_ERROR, "Wait for %d: %d\n", (int)request->pid, errno); } else { int normal = (WIFEXITED(status) && WEXITSTATUS(status) == 0) || (killed && WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM); char *reason = WIFEXITED(status) ? "with status" : WIFSIGNALED(status) ? "on signal" : "with unknown status"; int value = WIFEXITED(status) ? WEXITSTATUS(status) : WIFSIGNALED(status) ? WTERMSIG(status) : status; do_log(normal ? D_CHILD : L_ERROR, "Child %d exited %s %d.\n", (int)request->pid, reason, value); } free(request); return 1; }