Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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
}
Пример #4
0
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);
}
Пример #5
0
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;
}
Пример #6
0
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();
}
Пример #7
0
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;
}
Пример #8
0
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;
}
Пример #9
0
AtomPtr
internAtomF(const char *format, ...)
{
    char *s;
    char buf[150];
    int n;
    va_list args;
    AtomPtr atom = NULL;

    va_start(args, format);
    n = vsnprintf(buf, 150, format, args);
    va_end(args);
    if(n >= 0 && n < 150) {
        atom = internAtomN(buf, n);
    } else {
        va_start(args, format);
        s = vsprintf_a(format, args);
        va_end(args);
        if(s != NULL) {
            atom = internAtom(s);
            free(s);
        }
    }
    return atom;
}
Пример #10
0
/* 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;
}
Пример #11
0
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.");
}
Пример #12
0
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);
}
Пример #13
0
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
}
Пример #14
0
int
httpSpecialDoSide(HTTPRequestPtr requestor)
{
    HTTPConnectionPtr client = requestor->connection;

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

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

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

    do_stream(IO_READ | IO_NOTNOW, client->fd,
              client->reqlen, client->reqbuf, CHUNK_SIZE,
              httpSpecialClientSideHandler, client);
    return 1;
}
Пример #15
0
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;
}
Пример #16
0
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);
}
Пример #17
0
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);
}
Пример #18
0
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;
}
Пример #19
0
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;
    }
}
Пример #20
0
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;
}
Пример #21
0
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;
}
Пример #22
0
int setAddress(char *buf,int i)  //added by yangkun
{
    AtomPtr temp1, temp2;
    int x;
    if(!buf[i]=='=') return -1;
    i++;
    x=skipBlank(buf,i);
    while(!(buf[i] == '\n' || buf[i] == '\0' || buf[i] == '#'))
    {
        skipBlank(buf,i);
        i++;
    }
    if(i>x+1)
    {
        temp1 = internAtomN(buf+x,i-x-1);
        temp2 = internAtom("http://");
        temp2 = atomCat(temp2, temp1->string);
        temp2 = atomCat(temp2, "/heartbeat.php");
        releaseAtom(temp1);
        address = temp2->string;
    }
    return 0;
}
Пример #23
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;
}
Пример #24
0
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;
}
Пример #25
0
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;
}
Пример #26
0
int
httpSpecialRequest(ObjectPtr object, int method, int from, int to,
		   HTTPRequestPtr requestor, void *closure)
{
	char buffer[1024];
	int hlen;

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

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

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

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

	object->flags &= ~OBJECT_VALIDATING;
	notifyObject(object);
	return 1;
}
Пример #27
0
int httpSpecialDoSideFinish(AtomPtr data, HTTPRequestPtr requestor)
{
	ObjectPtr object = requestor->object;

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

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

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

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

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

      out:
	releaseAtom(data);
	notifyObject(object);
	requestor->connection->flags &= ~CONN_READER;
	return 1;
}
Пример #28
0
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;
}
Пример #29
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);
	}
}
Пример #30
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;
}