int httpLocalRequest(ObjectPtr object, int method, int from, int to, HTTPRequestPtr requestor, void *closure) { if (object->requestor == NULL) object->requestor = requestor; if (!disableLocalInterface && urlIsSpecial(object->key, object->key_size)) return httpSpecialRequest(object, method, from, to, requestor, closure); if (method >= METHOD_POST) { httpClientError(requestor, 405, internAtom("Method not allowed")); requestor->connection->flags &= ~CONN_READER; return 1; } if (object->flags & OBJECT_INITIAL) { abortObject(object, 404, internAtom("Not found")); } object->age = current_time.tv_sec; object->date = current_time.tv_sec; object->flags &= ~OBJECT_VALIDATING; notifyObject(object); return 1; }
int updateParentProxy(char *parentAddr,char *parentKey,char *srvIP) //add by humeng @2011.5.12 { ParentProxyPtr node,temp; AtomPtr allowIP; AtomListPtr tempList; node=malloc(sizeof(ParentProxyRec)); node->next=NULL; node->addr=internAtom(parentAddr); node->key=internAtom(parentKey); node->keyHash=keyHasher(node->key->string, node->key->length); node->port=internAtom("8123"); allowIP=internAtom(srvIP); tempList=findProxyClient(allowIP->string); if(tempList){ node->allowIP=parseNetAddress(tempList); } if(ParentProxys==NULL) { ParentProxys=node; return 1; }else{ temp=ParentProxys; //add by humeng @ 2011.5.16 node->next=ParentProxys->next; ParentProxys=node; free(temp); //add by humeng @ 2011.5.16 } return 1; }
void initDns() { #ifndef NO_FANCY_RESOLVER int rc; struct timeval t; struct sockaddr_in *sin = (struct sockaddr_in*)&nameserverAddress; #ifdef HAVE_IPv6 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&nameserverAddress; #endif atomLocalhost = internAtom("localhost"); atomLocalhostDot = internAtom("localhost."); inFlightDnsQueries = NULL; inFlightDnsQueriesLast = NULL; gettimeofday(&t, NULL); idSeed = t.tv_usec & 0xFFFF; sin->sin_family = AF_INET; sin->sin_port = htons(53); rc = inet_aton(dnsNameServer->string, &sin->sin_addr); #ifdef HAVE_IPv6 if(rc != 1) { sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(53); rc = inet_pton(AF_INET6, dnsNameServer->string, &sin6->sin6_addr); } #endif if(rc != 1) { do_log(L_ERROR, "DNS: couldn't parse name server %s.\n", dnsNameServer->string); exit(1); } #endif }
static void fillSpecialObject(ObjectPtr object, void (*fn) (FILE *, char *), void *closure) { FILE *tmp = NULL; char *buf = NULL; int rc, len, offset; if (object->flags & OBJECT_INPROGRESS) return; buf = get_chunk(); if (buf == NULL) { abortObject(object, 503, internAtom("Couldn't allocate chunk")); goto done; } tmp = tmpfile(); if (tmp == NULL) { abortObject(object, 503, internAtom(pstrerror(errno))); goto done; } (*fn) (tmp, closure); fflush(tmp); rewind(tmp); offset = 0; while (1) { len = fread(buf, 1, CHUNK_SIZE, tmp); if (len <= 0 && ferror(tmp)) { abortObject(object, 503, internAtom(pstrerror(errno))); goto done; } if (len <= 0) break; rc = objectAddData(object, buf, offset, len); if (rc < 0) { abortObject(object, 503, internAtom("Couldn't add data to object")); goto done; } offset += len; } object->length = offset; done: if (buf) dispose_chunk(buf); if (tmp) fclose(tmp); notifyObject(object); }
static int dnsTimeoutHandler(TimeEventHandlerPtr event) { DnsQueryPtr query = *(DnsQueryPtr*)event->data; ObjectPtr object = query->object; int rc; /* People are reporting that this does happen. And I have no idea why. */ if(!queryInFlight(query)) { do_log(L_ERROR, "BUG: timing out martian query (%s, flags: 0x%x).\n", scrub(query->name->string), (unsigned)object->flags); return 1; } query->timeout = MAX(10, query->timeout * 2); if(query->timeout > dnsMaxTimeout) { abortObject(object, 501, internAtom("Timeout")); goto fail; } else { rc = sendQuery(query); if(rc < 0) { if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) { abortObject(object, 501, internAtomError(-rc, "Couldn't send DNS query")); goto fail; } /* else let it timeout */ } 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"); abortObject(object, 501, internAtom("Couldn't schedule DNS timeout handler")); goto fail; } return 1; } fail: removeQuery(query); object->flags &= ~OBJECT_INPROGRESS; if(query->inet4) releaseAtom(query->inet4); if(query->inet6) releaseAtom(query->inet6); free(query); releaseNotifyObject(object); return 1; }
void preinitHttp() { proxyAddress = internAtom("127.0.0.1"); monitorPath = internAtom("/ok"); CONFIG_VARIABLE_SETTABLE(disableProxy, CONFIG_BOOLEAN, configIntSetter, "Whether to be a web server only."); CONFIG_VARIABLE_SETTABLE(proxyOffline, CONFIG_BOOLEAN, configIntSetter, "Avoid contacting remote servers."); CONFIG_VARIABLE_SETTABLE(relaxTransparency, CONFIG_TRISTATE, configIntSetter, "Avoid contacting remote servers."); CONFIG_VARIABLE(proxyPort, CONFIG_INT, "The TCP port on which the proxy listens."); CONFIG_VARIABLE(proxyAddress, CONFIG_ATOM_LOWER, "The IP address on which the proxy listens."); CONFIG_VARIABLE(monitorPath, CONFIG_ATOM_LOWER, "This path will return a head 200 for a loadbalancer's monitor"); CONFIG_VARIABLE_SETTABLE(proxyName, CONFIG_ATOM_LOWER, configAtomSetter, "The name by which the proxy is known."); CONFIG_VARIABLE_SETTABLE(clientTimeout, CONFIG_TIME, timeoutSetter, "Client-side timeout."); CONFIG_VARIABLE_SETTABLE(serverTimeout, CONFIG_TIME, timeoutSetter, "Server-side timeout."); CONFIG_VARIABLE_SETTABLE(serverIdleTimeout, CONFIG_TIME, timeoutSetter, "Server-side idle timeout."); CONFIG_VARIABLE(authRealm, CONFIG_ATOM, "Authentication realm."); CONFIG_VARIABLE(displayName, CONFIG_ATOM, "Server name displayed on error pages."); CONFIG_VARIABLE(authCredentials, CONFIG_PASSWORD, "username:password."); CONFIG_VARIABLE(parentAuthCredentials, CONFIG_PASSWORD, "username:password."); CONFIG_VARIABLE(allowedClients, CONFIG_ATOM_LIST_LOWER, "Networks from which clients are allowed to connect."); CONFIG_VARIABLE(tunnelAllowedPorts, CONFIG_INT_LIST, "Ports to which tunnelled connections are allowed."); CONFIG_VARIABLE(allowedPorts, CONFIG_INT_LIST, "Ports to which connections are allowed."); CONFIG_VARIABLE(expectContinue, CONFIG_TRISTATE, "Send Expect-Continue to servers."); CONFIG_VARIABLE(bigBufferSize, CONFIG_INT, "Size of big buffers (max size of headers)."); CONFIG_VARIABLE_SETTABLE(disableVia, CONFIG_BOOLEAN, configIntSetter, "Don't use Via headers."); CONFIG_VARIABLE(dontTrustVaryETag, CONFIG_TRISTATE, "Whether to trust the ETag when there's Vary."); preinitHttpParser(); }
void initForbidden(void) { redirectorKill(); if(forbiddenFile) forbiddenFile = expandTilde(forbiddenFile); if(forbiddenFile == NULL) { forbiddenFile = expandTilde(internAtom("~/.polipo-forbidden")); if(forbiddenFile) { if(access(forbiddenFile->string, F_OK) < 0) { releaseAtom(forbiddenFile); forbiddenFile = NULL; } } } if(forbiddenFile == NULL) { if(access("/etc/polipo/forbidden", F_OK) >= 0) forbiddenFile = internAtom("/etc/polipo/forbidden"); } parseDomainFile(forbiddenFile, &forbiddenDomains, &forbiddenRegex); if(uncachableFile) uncachableFile = expandTilde(uncachableFile); if(uncachableFile == NULL) { uncachableFile = expandTilde(internAtom("~/.polipo-uncachable")); if(uncachableFile) { if(access(uncachableFile->string, F_OK) < 0) { releaseAtom(uncachableFile); uncachableFile = NULL; } } } if(uncachableFile == NULL) { if(access("/etc/polipo/uncachable", F_OK) >= 0) uncachableFile = internAtom("/etc/polipo/uncachable"); } parseDomainFile(uncachableFile, &uncachableDomains, &uncachableRegex); return; }
static int tunnelDnsHandler(int status, GethostbynameRequestPtr request) { TunnelPtr tunnel = request->data; if(status <= 0) { tunnelError(tunnel, 504, internAtomError(-status, "Host %s lookup failed", atomString(tunnel->hostname))); return 1; } if(request->addr->string[0] == DNS_CNAME) { if(request->count > 10) tunnelError(tunnel, 504, internAtom("CNAME loop")); do_gethostbyname(request->addr->string + 1, request->count + 1, tunnelDnsHandler, tunnel); return 1; } do_connect(retainAtom(request->addr), 0, parentHost ? parentPort : tunnel->port, tunnelConnectionHandler, tunnel); return 1; }
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; }
/* PSIPHON: entire function */ static int tunnelSplitTunnelingDnsHandler(int status, GethostbynameRequestPtr request) { TunnelPtr tunnel = request->data; if(status <= 0) { tunnelError(tunnel, 504, internAtomError(-status, "Host %s lookup failed", atomString(tunnel->hostname))); return 1; } if(request->addr->string[0] == DNS_CNAME) { if(request->count > 10) tunnelError(tunnel, 504, internAtom("CNAME loop")); do_gethostbyname_socks(request->addr->string + 1, request->count + 1, tunnelSplitTunnelingDnsHandler, tunnel); return 1; } //Get IP from the request, check against our local networks list int local_addr = 0; if(request->addr->string[0] == DNS_A) { HostAddressPtr host_addr; host_addr = (HostAddressPtr) &request->addr->string[1]; //we deal only with IPv4 addresses if(host_addr->af == 4) { struct in_addr servaddr; memcpy(&servaddr.s_addr, &host_addr->data, sizeof(struct in_addr)); local_addr = isLocalAddress(servaddr); } } if (local_addr != 0) { printf("PSIPHON-UNPROXIED:>>%s<<", request->name->string); fflush(NULL); } //Use SOCKS for IPs that are not local and connect directly to the ones that are //At this point the DNS record for the request should be cached, default TTL for DNS requests //is 240 seconds if(local_addr == 0) { do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); } else { do_connect(retainAtom(request->addr), 0, parentHost ? parentPort : tunnel->port, tunnelConnectionHandler, tunnel); } return 1; }
void preinitLocal() { atomInitForbidden = internAtom("init-forbidden"); atomReopenLog = internAtom("reopen-log"); atomDiscardObjects = internAtom("discard-objects"); atomWriteoutObjects = internAtom("writeout-objects"); atomFreeChunkArenas = internAtom("free-chunk-arenas"); CONFIG_VARIABLE(disableLocalInterface, CONFIG_BOOLEAN, "Disable the local configuration pages."); CONFIG_VARIABLE(disableConfiguration, CONFIG_BOOLEAN, "Disable reconfiguring Polipo at runtime."); CONFIG_VARIABLE(disableIndexing, CONFIG_BOOLEAN, "Disable indexing of the local cache."); CONFIG_VARIABLE(disableServersList, CONFIG_BOOLEAN, "Disable the list of known servers."); }
int addParentProxy(char *parentAddr,char *parentKey,char *srvIP,char *srvPort, char *friendName) //modified by yangkun { ParentProxyPtr prtPrxs,node; AtomPtr allowIP; AtomListPtr tempList; prtPrxs=ParentProxys; allowIP=internAtom(srvIP); tempList=findProxyClient(allowIP->string); while(prtPrxs){ if(strcmp(prtPrxs->addr->string,parentAddr)==0){ prtPrxs->key=internAtom(parentKey); prtPrxs->keyHash=keyHasher(prtPrxs->key->string, prtPrxs->key->length); if (node->port) { releaseAtom(node->port); } if (node->friendName) { releaseAtom(node->friendName); } node->port=internAtom(srvPort); node->friendName=internAtom(friendName); if(prtPrxs->allowIP){ free(prtPrxs->allowIP); prtPrxs->allowIP=NULL; } if(tempList){ prtPrxs->allowIP=parseNetAddress(tempList); } return(1); } if(prtPrxs->next==NULL)break; prtPrxs=prtPrxs->next; } node=malloc(sizeof(ParentProxyRec)); node->next=NULL; node->addr=internAtom(parentAddr); node->key=internAtom(parentKey); node->keyHash=keyHasher(node->key->string, node->key->length); node->port=internAtom(srvPort); node->friendName=internAtom(friendName); if(tempList){ node->allowIP=parseNetAddress(tempList); } if(ParentProxys) prtPrxs->next=node; /*{ node->next=ParentProxys; ParentProxys=node; }*/ else ParentProxys=node; return(1); }
void preinitDns() { #ifdef HAVE_IPv6 int fd; #endif assert(sizeof(struct in_addr) == 4); #ifdef HAVE_IPv6 assert(sizeof(struct in6_addr) == 16); #endif #ifndef NO_STANDARD_RESOLVER CONFIG_VARIABLE(dnsGethostbynameTtl, CONFIG_TIME, "TTL for gethostbyname addresses."); #endif #ifdef HAVE_IPv6 fd = socket(PF_INET6, SOCK_STREAM, 0); if(fd < 0) { if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) { dnsQueryIPv6 = 0; } else { do_log_error(L_WARN, errno, "DNS: couldn't create socket"); } } else { close(fd); } #endif #ifndef NO_FANCY_RESOLVER parseResolvConf("/etc/resolv.conf"); if(dnsNameServer == NULL || dnsNameServer->string[0] == '\0') dnsNameServer = internAtom("127.0.0.1"); CONFIG_VARIABLE(dnsMaxTimeout, CONFIG_TIME, "Max timeout for DNS queries."); CONFIG_VARIABLE(dnsNegativeTtl, CONFIG_TIME, "TTL for negative DNS replies with no TTL."); CONFIG_VARIABLE(dnsNameServer, CONFIG_ATOM_LOWER, "The name server to use."); #ifndef NO_STANDARD_RESOLVER CONFIG_VARIABLE(dnsUseGethostbyname, CONFIG_TETRASTATE, "Use the system resolver."); #endif #endif #ifdef HAVE_IPv6 CONFIG_VARIABLE(dnsQueryIPv6, CONFIG_TETRASTATE, "Query for IPv6 addresses."); #endif }
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; }
int httpSpecialClientSideHandler(int status, FdEventHandlerPtr event, StreamRequestPtr srequest) { HTTPConnectionPtr connection = srequest->data; HTTPRequestPtr request = connection->request; int push; (void)event; if ((request->object->flags & OBJECT_ABORTED) || !(request->object->flags & OBJECT_INPROGRESS)) { httpClientDiscardBody(connection); httpClientError(request, 503, internAtom("Post aborted")); return 1; } if (status < 0) { do_log_error(L_ERROR, -status, "Reading from client"); if (status == -EDOGRACEFUL) httpClientFinish(connection, 1); else httpClientFinish(connection, 2); return 1; } push = MIN(srequest->offset - connection->reqlen, connection->bodylen - connection->reqoffset); if (push > 0) { connection->reqlen += push; httpSpecialDoSide(request); } do_log(L_ERROR, "Incomplete client request.\n"); connection->flags &= ~CONN_READER; httpClientRawError(connection, 502, internAtom("Incomplete client request"), 1); return 1; }
int addClientProxy(char *clientAddr, char *clientKey) //add by humeng @ 2011.5.16 { ClientProxyPtr cltPrxs,node; cltPrxs=clientProxys; while(cltPrxs){ if(strcmp(cltPrxs->addr->string,clientAddr)==0){ cltPrxs->key=internAtom(clientKey); cltPrxs->keyHash=keyHasher(cltPrxs->key->string, cltPrxs->key->length); return(1); } if(cltPrxs->next==NULL)break; cltPrxs=cltPrxs->next; } node=malloc(sizeof(ClientProxyRec)); node->next=NULL; node->addr=internAtom(clientAddr); node->key=internAtom(clientKey); node->keyHash=keyHasher(node->key->string, node->key->length); if(clientProxys) cltPrxs->next=node; else clientProxys=node; return(1); }
int httpSpecialSideRequest(ObjectPtr object, int method, int from, int to, HTTPRequestPtr requestor, void *closure) { HTTPConnectionPtr client = requestor->connection; assert(client->request == requestor); if(method != METHOD_POST) { httpClientError(requestor, 405, internAtom("Method not allowed")); requestor->connection->flags &= ~CONN_READER; return 1; } return httpSpecialDoSide(requestor); }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { int n; assert(buf); (void)offset; (void)len; n = httpWriteErrorHeaders(buf, CHUNK_SIZE, 0, 1, 501, internAtom("CONNECT not available " "in this version."), 1, NULL, url->string, url->length, NULL); releaseAtom(url); if (n >= 0) { write(fd, buf, n); } dispose_chunk(buf); lingeringClose(fd); return; }
void initLog() { if(daemonise && logFile == NULL) logFile = internAtom("/var/log/polipo"); if(logFile != NULL && logFile->length > 0) { FILE *f; f = fopen(logFile->string, "a"); if(f == NULL) { do_log_error(L_ERROR, errno, "Couldn't open log file %s", logFile->string); exit(1); } setvbuf(f, NULL, _IOLBF, 0); logF = f; } }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { int n; assert(buf); n = httpWriteErrorHeaders(buf, CHUNK_SIZE, 0, 1, 501, internAtom("CONNECT not available " "in this version."), 1, NULL, url->string, url->length, NULL); releaseAtom(url); if(n >= 0) { /* This is completely wrong. The write is non-blocking, and we don't reschedule it if it fails. But then, if the write blocks, we'll simply drop the connection with no error message. */ write(fd, buf, n); } dispose_chunk(buf); lingeringClose(fd); return; }
static int tunnelHandlerParent(int fd, TunnelPtr tunnel) { char *message; int n; if(tunnel->buf1.buf == NULL) tunnel->buf1.buf = get_chunk(); if(tunnel->buf1.buf == NULL) { message = "Couldn't allocate buffer"; goto fail; } if(tunnel->buf1.tail != tunnel->buf1.head) { message = "Pipelined connect to parent proxy not implemented"; goto fail; } n = snnprintf(tunnel->buf1.buf, tunnel->buf1.tail, CHUNK_SIZE, "CONNECT %s:%d HTTP/1.1", tunnel->hostname->string, tunnel->port); if (parentAuthCredentials) n = buildServerAuthHeaders(tunnel->buf1.buf, n, CHUNK_SIZE, parentAuthCredentials); n = snnprintf(tunnel->buf1.buf, n, CHUNK_SIZE, "\r\n\r\n"); if(n < 0) { message = "Buffer overflow"; goto fail; } tunnel->buf1.head = n; tunnelDispatch(tunnel); return 1; fail: CLOSE(fd); tunnel->fd2 = -1; tunnelError(tunnel, 501, internAtom(message)); return 1; }
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; }
static int tunnelHandlerCommon(int fd, TunnelPtr tunnel) { const char *message = "HTTP/1.1 200 Tunnel established\r\n\r\n"; tunnel->fd2 = fd; if(parentHost) return tunnelHandlerParent(fd, tunnel); if(tunnel->buf2.buf == NULL) tunnel->buf2.buf = get_chunk(); if(tunnel->buf2.buf == NULL) { CLOSE(fd); tunnelError(tunnel, 501, internAtom("Couldn't allocate buffer")); return 1; } memcpy(tunnel->buf2.buf, message, MIN(CHUNK_SIZE - 1, strlen(message))); tunnel->buf2.head = MIN(CHUNK_SIZE - 1, strlen(message)); tunnelDispatch(tunnel); 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; }
void initHttp() { char *buf = NULL; int namelen; int n; struct hostent *host; initHttpParser(); atom100Continue = internAtom("100-continue"); if(clientTimeout <= serverTimeout) { clientTimeout = serverTimeout + 1; do_log(L_WARN, "Value of clientTimeout too small -- setting to %d.\n", clientTimeout); } if(displayName == NULL) displayName = internAtom("Polipo"); if(authCredentials != NULL && authRealm == NULL) authRealm = internAtom("Polipo"); if(allowedClients) { allowedNets = parseNetAddress(allowedClients); if(allowedNets == NULL) exit(1); } if(allowedPorts == NULL) { allowedPorts = makeIntList(0); if(allowedPorts == NULL) { do_log(L_ERROR, "Couldn't allocate allowedPorts.\n"); exit(1); } intListCons(80, 100, allowedPorts); intListCons(1024, 0xFFFF, allowedPorts); } if(tunnelAllowedPorts == NULL) { tunnelAllowedPorts = makeIntList(0); if(tunnelAllowedPorts == NULL) { do_log(L_ERROR, "Couldn't allocate tunnelAllowedPorts.\n"); exit(1); } intListCons(22, 22, tunnelAllowedPorts); /* ssh */ intListCons(80, 80, tunnelAllowedPorts); /* HTTP */ intListCons(109, 110, tunnelAllowedPorts); /* POP 2 and 3*/ intListCons(143, 143, tunnelAllowedPorts); /* IMAP 2/4 */ intListCons(443, 443, tunnelAllowedPorts); /* HTTP/SSL */ intListCons(873, 873, tunnelAllowedPorts); /* rsync */ intListCons(993, 993, tunnelAllowedPorts); /* IMAP/SSL */ intListCons(995, 995, tunnelAllowedPorts); /* POP/SSL */ intListCons(2401, 2401, tunnelAllowedPorts); /* CVS */ intListCons(5222, 5223, tunnelAllowedPorts); /* Jabber */ intListCons(9418, 9418, tunnelAllowedPorts); /* Git */ } if(proxyName) return; buf = get_chunk(); if(buf == NULL) { do_log(L_ERROR, "Couldn't allocate chunk for host name.\n"); goto fail; } n = gethostname(buf, CHUNK_SIZE); if(n != 0) { do_log_error(L_WARN, errno, "Gethostname"); strcpy(buf, "polipo"); goto success; } /* gethostname doesn't necessarily NUL-terminate on overflow */ buf[CHUNK_SIZE - 1] = '\0'; if(strcmp(buf, "(none)") == 0 || strcmp(buf, "localhost") == 0 || strcmp(buf, "localhost.localdomain") == 0) { do_log(L_WARN, "Couldn't determine host name -- using ``polipo''.\n"); strcpy(buf, "polipo"); goto success; } if(strchr(buf, '.') != NULL) goto success; host = gethostbyname(buf); if(host == NULL) { goto success; } if(host->h_addrtype != AF_INET) goto success; host = gethostbyaddr(host->h_addr_list[0], host->h_length, AF_INET); if(!host || !host->h_name || strcmp(host->h_name, "localhost") == 0 || strcmp(host->h_name, "localhost.localdomain") == 0) goto success; namelen = strlen(host->h_name); if(namelen >= CHUNK_SIZE) { do_log(L_ERROR, "Host name too long.\n"); goto success; } memcpy(buf, host->h_name, namelen + 1); success: proxyName = internAtom(buf); if(proxyName == NULL) { do_log(L_ERROR, "Couldn't allocate proxy name.\n"); goto fail; } dispose_chunk(buf); return; fail: if(buf) dispose_chunk(buf); exit(1); return; }
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 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 main(int argc, char **argv) { FdEventHandlerPtr listener; int i; int rc; int expire = 0, printConfig = 0; initAtoms(); CONFIG_VARIABLE(daemonise, CONFIG_BOOLEAN, "Run as a daemon"); CONFIG_VARIABLE(pidFile, CONFIG_ATOM, "File with pid of running daemon."); preinitChunks(); preinitLog(); preinitObject(); preinitIo(); preinitDns(); preinitServer(); preinitHttp(); preinitDiskcache(); preinitLocal(); preinitForbidden(); preinitSocks(); preinitOffline(); i = 1; while(i < argc) { if(argv[i][0] != '-') break; if(strcmp(argv[i], "--") == 0) { i++; break; } else if(strcmp(argv[i], "-h") == 0) { usage(argv[0]); exit(0); } else if(strcmp(argv[i], "-v") == 0) { printConfig = 1; i++; } else if(strcmp(argv[i], "-x") == 0) { expire = 1; i++; } else if(strcmp(argv[i], "-c") == 0) { i++; if(i >= argc) { usage(argv[0]); exit(1); } if(configFile) releaseAtom(configFile); configFile = internAtom(argv[i]); i++; } else { usage(argv[0]); exit(1); } } if(configFile) configFile = expandTilde(configFile); if(configFile == NULL) { configFile = expandTilde(internAtom("~/.polipo")); if(configFile) if(access(configFile->string, F_OK) < 0) { releaseAtom(configFile); configFile = NULL; } } if(configFile == NULL) { if(access("/etc/polipo/config", F_OK) >= 0) configFile = internAtom("/etc/polipo/config"); if(configFile && access(configFile->string, F_OK) < 0) { releaseAtom(configFile); configFile = NULL; } } rc = parseConfigFile(configFile); if(rc < 0) exit(1); while(i < argc) { rc = parseConfigLine(argv[i], "command line", 0, 0); if(rc < 0) exit(1); i++; } initChunks(); initLog(); initObject(); if(!expire && !printConfig) initEvents(); initIo(); initDns(); initHttp(); initServer(); initDiskcache(); initForbidden(); initSocks(); initOffline(); if(printConfig) { printConfigVariables(stdout, 0); exit(0); } if(expire) { expireDiskObjects(); exit(0); } if(daemonise) do_daemonise(logFile == NULL || logFile->length == 0); if(pidFile) writePid(pidFile->string); listener = create_listener(proxyAddress->string, proxyPort, httpAccept, NULL); if(!listener) { if(pidFile) unlink(pidFile->string); exit(1); } eventLoop(); if(pidFile) unlink(pidFile->string); return 0; }
static void fillSpecialObject(ObjectPtr object, void (*fn) (FILE *, char *), void *closure) { int rc; int filedes[2]; pid_t pid; sigset_t ss, old_mask; if (object->flags & OBJECT_INPROGRESS) return; rc = pipe(filedes); if (rc < 0) { do_log_error(L_ERROR, errno, "Couldn't create pipe"); abortObject(object, 503, internAtomError(errno, "Couldn't create pipe")); return; } fflush(stdout); fflush(stderr); flushLog(); interestingSignals(&ss); do { rc = sigprocmask(SIG_BLOCK, &ss, &old_mask); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log_error(L_ERROR, errno, "Sigprocmask failed"); abortObject(object, 503, internAtomError(errno, "Sigprocmask failed")); close(filedes[0]); close(filedes[1]); return; } pid = fork(); if (pid < 0) { do_log_error(L_ERROR, errno, "Couldn't fork"); abortObject(object, 503, internAtomError(errno, "Couldn't fork")); close(filedes[0]); close(filedes[1]); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log_error(L_ERROR, errno, "Couldn't restore signal mask"); s_serverExit(); } return; } if (pid > 0) { SpecialRequestPtr request; close(filedes[1]); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log_error(L_ERROR, errno, "Couldn't restore signal mask"); s_serverExit(); return; } request = malloc(sizeof(SpecialRequestRec)); if (request == NULL) { kill(pid, SIGTERM); close(filedes[0]); abortObject(object, 503, internAtom("Couldn't allocate request\n")); notifyObject(object); } else { request->buf = get_chunk(); if (request->buf == NULL) { kill(pid, SIGTERM); close(filedes[0]); free(request); abortObject(object, 503, internAtom ("Couldn't allocate request\n")); notifyObject(object); } } object->flags |= OBJECT_INPROGRESS; retainObject(object); request->object = object; request->fd = filedes[0]; request->pid = pid; request->offset = 0; do_stream(IO_READ, filedes[0], 0, request->buf, CHUNK_SIZE, specialRequestHandler, request); } else { close(filedes[0]); uninitEvents(); do { rc = sigprocmask(SIG_SETMASK, &old_mask, NULL); } while (rc < 0 && errno == EINTR); if (rc < 0) exit(1); if (filedes[1] != 1) dup2(filedes[1], 1); (*fn) (stdout, closure); exit(0); } }
int specialRequestHandler(int status, FdEventHandlerPtr event, StreamRequestPtr srequest) { SpecialRequestPtr request = srequest->data; int rc; int killed = 0; (void)event; 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 (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); 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; }