/*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; }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { TunnelPtr tunnel; int port; char *p, *q; 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; } if(!intListMember(port, tunnelAllowedPorts)) { releaseAtom(url); tunnelError(tunnel, 403, internAtom("Forbidden port")); return; } 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); if(socksParentProxy) do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); 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 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 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; } }
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); }