Ejemplo n.º 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;
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
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);
	}
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
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;
}
Ejemplo n.º 7
0
static int
really_do_dns(AtomPtr name, ObjectPtr object)
{
    int rc;
    DnsQueryPtr query;
    AtomPtr message = NULL;
    int id;
    AtomPtr a = NULL;

    if(a == NULL) {
        if(name == atomLocalhost || name == atomLocalhostDot) {
            char s[1 + sizeof(HostAddressRec)];
            memset(s, 0, sizeof(s));
            s[0] = DNS_A;
            s[1] = 4;
            s[2] = 127;
            s[3] = 0;
            s[4] = 0;
            s[5] = 1;
            a = internAtomN(s, 1 + sizeof(HostAddressRec));
            if(a == NULL) {
                abortObject(object, 501,
                            internAtom("Couldn't allocate address"));
                notifyObject(object);
                errno = ENOMEM;
                return -1;
            }
        }
    }

    if(a == NULL) {
        struct in_addr ina;
        rc = inet_aton(name->string, &ina);
        if(rc == 1) {
            char s[1 + sizeof(HostAddressRec)];
            memset(s, 0, sizeof(s));
            s[0] = DNS_A;
            s[1] = 4;
            memcpy(s + 2, &ina, 4);
            a = internAtomN(s, 1 + sizeof(HostAddressRec));
            if(a == NULL) {
                abortObject(object, 501,
                            internAtom("Couldn't allocate address"));
                notifyObject(object);
                errno = ENOMEM;
                return -1;
            }
        }
    }
#ifdef HAVE_IPv6
    if(a == NULL)
        a = rfc2732(name);
#endif

    if(a) {
        object->headers = a;
        object->age = current_time.tv_sec;
        object->expires = current_time.tv_sec + 240;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
        notifyObject(object);
        return 0;
    }

    rc = establishDnsSocket();
    if(rc < 0) {
        do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
        message = internAtomError(-rc, "Couldn't establish DNS socket");
        goto fallback;
    }

    /* The id is used to speed up detecting replies to queries that
       are no longer current -- see dnsReplyHandler. */
    id = (idSeed++) & 0xFFFF;

    query = malloc(sizeof(DnsQueryRec));
    if(query == NULL) {
        do_log(L_ERROR, "Couldn't allocate DNS query.\n");
        message = internAtom("Couldn't allocate DNS query");
        goto fallback;
    }
    query->id = id;
    query->inet4 = NULL;
    query->inet6 = NULL;
    query->name = name;
    query->time = current_time.tv_sec;
    query->object = retainObject(object);
    query->timeout = 4;
    query->timeout_handler = NULL;
    query->next = NULL;

    query->timeout_handler = 
        scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
                          sizeof(query), &query);
    if(query->timeout_handler == NULL) {
        do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
        message = internAtom("Couldn't schedule DNS timeout handler");
        goto free_fallback;
    }
    insertQuery(query);

    object->flags |= OBJECT_INPROGRESS;
    rc = sendQuery(query);
    if(rc < 0) {
        if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
            object->flags &= ~OBJECT_INPROGRESS;
            message = internAtomError(-rc, "Couldn't send DNS query");
            goto remove_fallback;
        }
        /* else let it timeout */
    }
    releaseAtom(message);
    return 1;

 remove_fallback:
    removeQuery(query);
 free_fallback:
    releaseObject(query->object);
    cancelTimeEvent(query->timeout_handler);
    free(query);
 fallback:
    if(dnsUseGethostbyname >= 1) {
        releaseAtom(message);
        do_log(L_WARN, "Falling back on gethostbyname.\n");
        return really_do_gethostbyname(name, object);
    } else {
        abortObject(object, 501, message);
        notifyObject(object);
        return 1;
    }
}
Ejemplo n.º 8
0
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
    struct hostent *host;
    char *s;
    AtomPtr a;
    int i, j;
    int error;

    host = gethostbyname(name->string);
    if(host == NULL) {
        switch(h_errno) {
        case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
        case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
        case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
        case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
        default: error = EUNKNOWN; break;
        }
        if(error == EDNS_HOST_NOT_FOUND) {
            object->headers = NULL;
            object->age = current_time.tv_sec;
            object->expires = current_time.tv_sec + dnsNegativeTtl;
            object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
            object->flags &= ~OBJECT_INPROGRESS;
            notifyObject(object);
            return 0;
        } else {
            do_log_error(L_ERROR, error, "Gethostbyname failed");
            abortObject(object, 404, 
                        internAtomError(error, "Gethostbyname failed"));
            object->flags &= ~OBJECT_INPROGRESS;
            notifyObject(object);
            return 0;
        }
    }
    if(host->h_addrtype != AF_INET) {
        do_log(L_ERROR, "Address is not AF_INET.\n");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404, internAtom("Address is not AF_INET"));
        notifyObject(object);
        return -1;
    }
    if(host->h_length != sizeof(struct in_addr)) {
        do_log(L_ERROR, "Address size inconsistent.\n");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404, internAtom("Address size inconsistent"));
        notifyObject(object);
        return 0;
    }
    i = 0;
    while(host->h_addr_list[i] != NULL) i++;
    s = malloc(1 + i * sizeof(HostAddressRec));
    if(s == NULL) {
        a = NULL;
    } else {
        memset(s, 0, 1 + i * sizeof(HostAddressRec));
        s[0] = DNS_A;
        for(j = 0; j < i; j++) {
            s[j * sizeof(HostAddressRec) + 1] = 4;
            memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
                   sizeof(struct in_addr));
        }
        a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
        free(s);
    }
    if(!a) {
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 501, internAtom("Couldn't allocate address"));
        notifyObject(object);
        return 0;
    }
    object->headers = a;
    object->age = current_time.tv_sec;
    object->expires = current_time.tv_sec + dnsGethostbynameTtl;
    object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
    notifyObject(object);
    return 0;

}
Ejemplo n.º 9
0
static int
really_do_gethostbyname(AtomPtr name, ObjectPtr object)
{
    struct addrinfo *ai, *entry, hints;
    int rc;
    int error, i;
    char buf[1024];
    AtomPtr a;

    a = rfc2732(name);
    if(a) {
        object->headers = a;
        object->age = current_time.tv_sec;
        object->expires = current_time.tv_sec + 240;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
        notifyObject(object);
        return 0;
    }

    memset(&hints, 0, sizeof(hints));
    hints.ai_protocol = IPPROTO_TCP;
    if(dnsQueryIPv6 <= 0)
        hints.ai_family = AF_INET;
    else if(dnsQueryIPv6 >= 3)
        hints.ai_family = AF_INET6;

    rc = getaddrinfo(name->string, NULL, &hints, &ai);

    switch(rc) {
    case 0: error = 0; break;
    case EAI_FAMILY:
#ifdef EAI_ADDRFAMILY
    case EAI_ADDRFAMILY:
#endif
    case EAI_SOCKTYPE:
        error = EAFNOSUPPORT; break;
    case EAI_BADFLAGS: error = EINVAL; break;
    case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
#ifdef EAI_NONAME
    case EAI_NONAME:
#endif
#ifdef EAI_NODATA
    case EAI_NODATA:
#endif
        error = EDNS_NO_ADDRESS; break;
    case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
    case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
#ifdef EAI_MEMORY
    case EAI_MEMORY: error = ENOMEM; break;
#endif
    case EAI_SYSTEM: error = errno; break;
    default: error = EUNKNOWN;
    }

    if(error == EDNS_NO_ADDRESS) {
        object->headers = NULL;
        object->age = current_time.tv_sec;
        object->expires = current_time.tv_sec + dnsNegativeTtl;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
        notifyObject(object);
        return 0;
    } else if(error) {
        do_log_error(L_ERROR, error, "Getaddrinfo failed");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404,
                    internAtomError(error, "Getaddrinfo failed"));
        notifyObject(object);
        return 0;
    }

    entry = ai;
    buf[0] = DNS_A;
    i = 0;
    while(entry) {
        HostAddressRec host;
        int host_valid = 0;
        if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
            if(dnsQueryIPv6 < 3) {
                host.af = 4;
                memset(host.data, 0, sizeof(host.data));
                memcpy(&host.data,
                       &((struct sockaddr_in*)entry->ai_addr)->sin_addr, 
                       4);
                host_valid = 1;
            }
        } else if(entry->ai_family == AF_INET6 && 
                  entry->ai_protocol == IPPROTO_TCP) {
            if(dnsQueryIPv6 > 0) {
                host.af = 6;
                memset(&host.data, 0, sizeof(host.data));
                memcpy(&host.data,
                       &((struct sockaddr_in6*)entry->ai_addr)->sin6_addr, 
                       16);
                host_valid = 1;
            }
        }
        if(host_valid) {
            if(i >= 1024 / sizeof(HostAddressRec) - 2) {
                do_log(L_ERROR, "Too many addresses for host %s\n", 
                       name->string);
                break;
            }
            memcpy(buf + 1 + i * sizeof(HostAddressRec), 
                   &host, sizeof(HostAddressRec));
            i++;
        }
        entry = entry->ai_next;
    }
    freeaddrinfo(ai);
    if(i == 0) {
        do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 404,
                    internAtom("Getaddrinfo returned no useful addresses"));
        notifyObject(object);
        return 0;
    }

    if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
        qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);

    a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
    if(a == NULL) {
        object->flags &= ~OBJECT_INPROGRESS;
        abortObject(object, 501, internAtom("Couldn't allocate address"));
        notifyObject(object);
        return 0;
    }
    object->headers = a;
    object->age = current_time.tv_sec;
    object->expires = current_time.tv_sec + dnsGethostbynameTtl;
    object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
    notifyObject(object);
    return 0;
}
Ejemplo n.º 10
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();

    /* Block signals that we handle specially until the child can
       disable the handlers. */
    interestingSignals(&ss);
    /* I'm a little confused.  POSIX doesn't allow EINTR here, but I
       think that both Linux and SVR4 do. */
    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");
            polipoExit();
        }
        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");
            polipoExit();
            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);
            return;
        } 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);
                return;
            }
        }
        object->flags |= OBJECT_INPROGRESS;
        retainObject(object);
        request->object = object;
        request->fd = filedes[0];
        request->pid = pid;
        request->offset = 0;
        /* Under any sensible scheduler, the child will run before the
           parent.  So no need for IO_NOTNOW. */
        do_stream(IO_READ, filedes[0], 0, request->buf, CHUNK_SIZE,
                  specialRequestHandler, request);
    } else {
        /* child */
        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);
    }
}