Exemple #1
0
/* Called when we receive a connection on the listening socket. */
static void
accept_bittorrent_peer_connection(void *____)
{
	struct sockaddr_in addr;
	int peer_sock;
	int addrlen = sizeof(addr);
	struct bittorrent_peer_connection *peer;
	struct read_buffer *buffer;

	peer_sock = accept(bittorrent_socket, (struct sockaddr *) &addr, &addrlen);
	if (peer_sock < 0) return;

	if (set_nonblocking_fd(peer_sock) < 0) {
		close(peer_sock);
		return;
	}

	peer = init_bittorrent_peer_connection(peer_sock);
	if (!peer) {
		close(peer_sock);
		return;
	}

	peer->remote.initiater = 1;

	/* Just return. Failure is handled by alloc_read_buffer(). */
	buffer = alloc_read_buffer(peer->socket);
	if (!buffer) return;

	read_from_socket(peer->socket, buffer, connection_state(S_TRANS),
			 read_bittorrent_peer_handshake);

	add_to_list(bittorrent_peer_connections, peer);
}
Exemple #2
0
int
start_thread(void (*fn)(void *, int), void *ptr, int l)
{
	int p[2];
	pid_t pid;

	if (c_pipe(p) < 0) return -1;
	if (set_nonblocking_fd(p[0]) < 0) return -1;
	if (set_nonblocking_fd(p[1]) < 0) return -1;

	pid = fork();
	if (!pid) {
		struct terminal *term;

		/* Close input in this thread; otherwise, if it will live
		 * longer than its parent, it'll block the terminal until it'll
		 * quit as well; this way it will hopefully just die unseen and
		 * in background, causing no trouble. */
		/* Particularly, when async dns resolving was in progress and
		 * someone quitted ELinks, it could make a delay before the
		 * terminal would be really freed and returned to shell. */
		foreach (term, terminals)
			if (term->fdin > 0)
				close(term->fdin);

		close(p[0]);
		fn(ptr, p[1]);
		write(p[1], "x", 1);
		close(p[1]);
		/* We use _exit() here instead of exit(), see
		 * http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC6 for
		 * reasons. Fixed by Sven Neumann <*****@*****.**>. */
		_exit(0);
	}
	if (pid == -1) {
		close(p[0]);
		close(p[1]);
		return -1;
	}

	close(p[1]);
	return p[0];
}
Exemple #3
0
void
mailcap_protocol_handler(struct connection *conn)
{
#ifdef HAVE_FORK
	unsigned char *script, *ref;
	pid_t pid;
	struct connection_state state = connection_state(S_OK);
	int pipe_read[2], check;

	/* security checks */
	if (!conn->referrer || conn->referrer->protocol != PROTOCOL_MAILCAP) {
		goto bad;
	}
	ref = get_uri_string(conn->referrer, URI_DATA);
	if (!ref) {
		goto bad;
	}
	check = strcmp(ref, "elmailcap");
	mem_free(ref);
	if (check) goto bad;
	
	script = get_uri_string(conn->uri, URI_DATA);
	if (!script) {
		state = connection_state(S_OUT_OF_MEM);
		goto end2;
	}

	if (c_pipe(pipe_read)) {
		state = connection_state_for_errno(errno);
		goto end1;
	}

	pid = fork();
	if (pid < 0) {
		state = connection_state_for_errno(errno);
		goto end0;
	}
	if (!pid) {
		if (dup2(pipe_read[1], STDOUT_FILENO) < 0) {
			_exit(2);
		}
		/* We implicitly chain stderr to ELinks' stderr. */
		close_all_non_term_fd();

		if (execl("/bin/sh", "/bin/sh", "-c", script, (char *) NULL)) {
			_exit(3);
		}

	} else { /* ELinks */
		mem_free(script);

		if (!init_http_connection_info(conn, 1, 0, 1)) {
			close(pipe_read[0]); close(pipe_read[1]);
			return;
		}

		close(pipe_read[1]);
		conn->socket->fd = pipe_read[0];

		conn->data_socket->fd = -1;
		conn->cgi = 1;
		set_nonblocking_fd(conn->socket->fd);

		get_request(conn);
		return;
	}

end0:
	close(pipe_read[0]); close(pipe_read[1]);
end1:
	mem_free(script);
end2:
	abort_connection(conn, state);
	return;
#endif
bad:
	abort_connection(conn, connection_state(S_BAD_URL));
}
Exemple #4
0
void
smb_protocol_handler(struct connection *conn)
{
	int smb_pipe[2] = { -1, -1 };
	int header_pipe[2] = { -1, -1 };
	pid_t cpid;

	if (c_pipe(smb_pipe) || c_pipe(header_pipe)) {
		int s_errno = errno;

		if (smb_pipe[0] >= 0) close(smb_pipe[0]);
		if (smb_pipe[1] >= 0) close(smb_pipe[1]);
		if (header_pipe[0] >= 0) close(header_pipe[0]);
		if (header_pipe[1] >= 0) close(header_pipe[1]);
		abort_connection(conn, connection_state_for_errno(s_errno));
		return;
	}
	conn->from = 0;
	conn->unrestartable = 1;
	find_auth(conn->uri); /* remember username and password */

	cpid = fork();
	if (cpid == -1) {
		int s_errno = errno;

		close(smb_pipe[0]);
		close(smb_pipe[1]);
		close(header_pipe[0]);
		close(header_pipe[1]);
		retry_connection(conn, connection_state_for_errno(s_errno));
		return;
	}

	if (!cpid) {
		dup2(open("/dev/null", O_RDONLY), 0);
		close(1);
		close(2);
		data_out = fdopen(smb_pipe[1], "w");
		header_out = fdopen(header_pipe[1], "w");

		if (!data_out || !header_out) exit(1);

		close(smb_pipe[0]);
		close(header_pipe[0]);

		/* There may be outgoing data in stdio buffers
		 * inherited from the parent process.  The parent
		 * process is going to write this data, so the child
		 * process must not do that.  Closing the file
		 * descriptors ensures this.
		 *
		 * FIXME: If something opens more files and gets the
		 * same file descriptors and does not close them
		 * before exit(), then stdio may attempt to write the
		 * buffers to the wrong files.  This might happen for
		 * example if libsmbclient calls syslog().  */

		close_all_fds_but_two(smb_pipe[1], header_pipe[1]);
		do_smb(conn);

	} else {
		struct read_buffer *buf2;

		conn->data_socket->fd = smb_pipe[0];
		conn->socket->fd = header_pipe[0];
		set_nonblocking_fd(conn->data_socket->fd);
		set_nonblocking_fd(conn->socket->fd);
		close(smb_pipe[1]);
		close(header_pipe[1]);
		buf2 = alloc_read_buffer(conn->socket);
		if (!buf2) {
			close_socket(conn->data_socket);
			close_socket(conn->socket);
			abort_connection(conn, connection_state(S_OUT_OF_MEM));
			return;
		}
		read_from_socket(conn->socket, buf2,
				 connection_state(S_CONN), smb_got_header);
	}
}
Exemple #5
0
void
connect_socket(struct socket *csocket, struct connection_state state)
{
	int sock = -1;
	struct connect_info *connect_info = csocket->connect_info;
	int i;
	int trno = connect_info->triedno;
	int only_local = get_cmd_opt_bool("localhost");
	int saved_errno = 0;
	int at_least_one_remote_ip = 0;
#ifdef CONFIG_IPV6
	int try_ipv6 = get_opt_bool("connection.try_ipv6", NULL);
#endif
	int try_ipv4 = get_opt_bool("connection.try_ipv4", NULL);
	/* We tried something but we failed in such a way that we would rather
	 * prefer the connection to retain the information about previous
	 * failures.  That is, we i.e. decided we are forbidden to even think
	 * about such a connection attempt.
	 * XXX: Unify with @local_only handling? --pasky */
	int silent_fail = 0;

	csocket->ops->set_state(csocket, state);

	/* Clear handlers, the connection to the previous RR really timed
	 * out and doesn't interest us anymore. */
	if (csocket->fd >= 0)
		close_socket(csocket);

	for (i = connect_info->triedno + 1; i < connect_info->addrno; i++) {
#ifdef CONFIG_IPV6
		struct sockaddr_in6 addr = *((struct sockaddr_in6 *) &connect_info->addr[i]);
		int family = addr.sin6_family;
#else
		struct sockaddr_in addr = *((struct sockaddr_in *) &connect_info->addr[i]);
		int family = addr.sin_family;
#endif
		int pf;
		int force_family = connect_info->ip_family;

		connect_info->triedno++;

		if (only_local) {
			int local = 0;
#ifdef CONFIG_IPV6
			if (family == AF_INET6)
				local = check_if_local_address6((struct sockaddr_in6 *) &addr);
			else
#endif
				local = check_if_local_address4((struct sockaddr_in *) &addr);

			/* This forbids connections to anything but local, if option is set. */
			if (!local) {
				at_least_one_remote_ip = 1;
				continue;
			}
		}

#ifdef CONFIG_IPV6
		if (family == AF_INET6) {
			if (!try_ipv6 || (force_family && force_family != 6)) {
				silent_fail = 1;
				continue;
			}
			pf = PF_INET6;

		} else
#endif
		if (family == AF_INET) {
			if (!try_ipv4 || (force_family && force_family != 4)) {
				silent_fail = 1;
				continue;
			}
			pf = PF_INET;

		} else {
			continue;
		}
		silent_fail = 0;

		sock = socket(pf, SOCK_STREAM, IPPROTO_TCP);
		if (sock == -1) {
			if (errno && !saved_errno) saved_errno = errno;
			continue;
		}

		if (set_nonblocking_fd(sock) < 0) {
			if (errno && !saved_errno) saved_errno = errno;
			close(sock);
			continue;
		}
		csocket->fd = sock;

#ifdef CONFIG_IPV6
		addr.sin6_port = htons(connect_info->port);
#else
		addr.sin_port = htons(connect_info->port);
#endif

		/* We can set csocket->protocol_family here even if the connection
		 * will fail, as we will use it only when it will be successfully
		 * established. At least I hope that noone else will want to do
		 * something else ;-). --pasky */
		/* And in fact we must set it early, because of EINPROGRESS.  */

#ifdef CONFIG_IPV6
		if (family == AF_INET6) {
			csocket->protocol_family = EL_PF_INET6;
			if (connect(sock, (struct sockaddr *) &addr,
					sizeof(struct sockaddr_in6)) == 0) {
				/* Success */
				complete_connect_socket(csocket, NULL, NULL);
				return;
			}
		} else
#endif
		{
			csocket->protocol_family = EL_PF_INET;
			if (connect(sock, (struct sockaddr *) &addr,
					sizeof(struct sockaddr_in)) == 0) {
				/* Success */
				complete_connect_socket(csocket, NULL, NULL);
				return;
			}
		}

		if (errno == EALREADY
#ifdef EWOULDBLOCK
		    || errno == EWOULDBLOCK
#endif
		    || errno == EINPROGRESS) {
			/* It will take some more time... */
			set_handlers(sock, NULL, (select_handler_T) connected,
				     (select_handler_T) dns_exception, csocket);
			csocket->ops->set_state(csocket, connection_state(S_CONN));
			return;
		}

		if (errno && !saved_errno) saved_errno = errno;

		close(sock);
	}

	assert(i >= connect_info->addrno);

	/* Tried everything, but it didn't help :(. */

	if (only_local && !saved_errno && at_least_one_remote_ip) {
		/* Yes we might hit a local address and fail in the process, but
		 * what matters is the last one because we do not know the
		 * previous one's errno, and the added complexity wouldn't
		 * really be worth it. */
		csocket->ops->done(csocket, connection_state(S_LOCAL_ONLY));
		return;
	}

	/* Retry reporting the errno state only if we already tried something
	 * new. Else use the S_DNS _progress_ state to make sure that no
	 * download callbacks will report any errors. */
	if (trno != connect_info->triedno && !silent_fail)
		state = connection_state_for_errno(errno);
	else if (trno == -1 && silent_fail)
		/* All failed. */
		state = connection_state(S_NO_FORCED_DNS);

	csocket->ops->retry(csocket, state);
}
Exemple #6
0
/* Returns negative if error, otherwise pasv socket's fd. */
int
get_pasv_socket(struct socket *ctrl_socket, struct sockaddr_storage *addr)
{
	struct sockaddr_in bind_addr4;
	struct sockaddr *bind_addr;
	struct sockaddr *pasv_addr = (struct sockaddr *) addr;
	size_t addrlen;
	int sock = -1;
	int syspf; /* Protocol Family given to system, not EL_PF_... */
	socklen_t len;
#ifdef CONFIG_IPV6
	struct sockaddr_in6 bind_addr6;

	if (ctrl_socket->protocol_family == EL_PF_INET6) {
		bind_addr = (struct sockaddr *) &bind_addr6;
		addrlen   = sizeof(bind_addr6);
		syspf     = PF_INET6;
	} else
#endif
	{
		bind_addr = (struct sockaddr *) &bind_addr4;
		addrlen   = sizeof(bind_addr4);
		syspf     = PF_INET;
	}

	memset(pasv_addr, 0, addrlen);
	memset(bind_addr, 0, addrlen);

	/* Get our endpoint of the control socket */
	len = addrlen;
	if (getsockname(ctrl_socket->fd, pasv_addr, &len)) {
sock_error:
		if (sock != -1) close(sock);
		ctrl_socket->ops->retry(ctrl_socket,
					connection_state_for_errno(errno));
		return -1;
	}

	/* Get a passive socket */

	sock = socket(syspf, SOCK_STREAM, IPPROTO_TCP);
	if (sock < 0)
		goto sock_error;

	/* Set it non-blocking */

	if (set_nonblocking_fd(sock) < 0)
		goto sock_error;

	/* Bind it to some port */

	memcpy(bind_addr, pasv_addr, addrlen);
#ifdef CONFIG_IPV6
	if (ctrl_socket->protocol_family == EL_PF_INET6)
		bind_addr6.sin6_port = 0;
	else
#endif
		bind_addr4.sin_port = 0;

	if (bind(sock, bind_addr, addrlen))
		goto sock_error;

	/* Get our endpoint of the passive socket and save it to port */

	len = addrlen;
	if (getsockname(sock, pasv_addr, &len))
		goto sock_error;

	/* Go listen */

	if (listen(sock, 1))
		goto sock_error;

	set_ip_tos_throughput(sock);

	return sock;
}
Exemple #7
0
/* Based on network/socket.c:get_pasv_socket() but modified to try and bind to a
 * port range instead of any port. */
struct connection_state
init_bittorrent_listening_socket(struct connection *conn)
{
	struct bittorrent_connection *bittorrent = conn->info;
	struct sockaddr_in addr, addr2;
	uint16_t port, max_port;
	int len;

	/* XXX: Always add the connection to the list even if we fail so we can
	 * safely assume it is in done_bittorrent_listening_socket(). */
	add_to_list(bittorrent_connections, bittorrent);

	/* Has the socket already been initialized? */
	if (!list_is_singleton(bittorrent_connections))
		return connection_state(S_OK);

	/* We could have bailed out from an earlier attempt. */
	if (bittorrent_socket != -1)
		close(bittorrent_socket);

	bittorrent_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (bittorrent_socket < 0)
		return connection_state_for_errno(errno);

	/* Set it non-blocking */

	if (set_nonblocking_fd(bittorrent_socket) < 0)
		return connection_state_for_errno(errno);

	/* Bind it to some port */

	port	 = get_opt_int("protocol.bittorrent.ports.min", NULL);
	max_port = get_opt_int("protocol.bittorrent.ports.max", NULL);

	memset(&addr, 0, sizeof(addr));
	addr.sin_port = htons(port);

	/* Repeatedly try the configured port range. */
	while (bind(bittorrent_socket, (struct sockaddr *) &addr, sizeof(addr))) {
		if (errno != EADDRINUSE)
			return connection_state_for_errno(errno);

		/* If all ports was in use fail with EADDRINUSE. */
		if (++port > max_port)
			return connection_state_for_errno(errno);

		memset(&addr, 0, sizeof(addr));
		addr.sin_port = htons(port);
	}

	/* Get the endpoint info about the new socket and save it */

	memset(&addr2, 0, sizeof(addr2));
	len = sizeof(addr2);
	if (getsockname(bittorrent_socket, (struct sockaddr *) &addr2, &len))
		return connection_state_for_errno(errno);

	bittorrent->port = ntohs(addr2.sin_port);

	/* Go listen */

	if (listen(bittorrent_socket, LISTEN_BACKLOG))
		return connection_state_for_errno(errno);

	set_ip_tos_throughput(bittorrent_socket);
	set_handlers(bittorrent_socket, accept_bittorrent_peer_connection,
		     NULL, NULL, NULL);

	return connection_state(S_OK);
}
Exemple #8
0
/* To reduce redundant error handling code [calls to abort_connection()]
 * most of the function is build around conditions that will assign the error
 * code to @state if anything goes wrong. The rest of the function will then just
 * do the necessary cleanups. If all works out we end up with @state being S_OK
 * resulting in a cache entry being created with the fragment data generated by
 * either reading the file content or listing a directory. */
void
file_protocol_handler(struct connection *connection)
{
	unsigned char *redirect_location = NULL;
	struct string page, name;
	struct connection_state state;
	int set_dir_content_type = 0;

	if (get_cmd_opt_bool((const unsigned char *)"anonymous")) {
		if (strcmp((const char *)connection->uri->string, "file:///dev/stdin")
		    || isatty(STDIN_FILENO)) {
			abort_connection(connection,
					 connection_state(S_FILE_ANONYMOUS));
			return;
		}
	}

#ifdef CONFIG_CGI
	if (!execute_cgi(connection)) return;
#endif /* CONFIG_CGI */

	/* Treat /dev/stdin in special way */
	if (!strcmp((const char *)connection->uri->string, "file:///dev/stdin")) {
		int fd = open("/dev/stdin", O_RDONLY);

		if (fd == -1) {
			abort_connection(connection, connection_state(-errno));
			return;
		}
		set_nonblocking_fd(fd);
		if (!init_http_connection_info(connection, 1, 0, 1)) {
			abort_connection(connection, connection_state(S_OUT_OF_MEM));
			close(fd);
			return;
		}
		connection->socket->fd = fd;
		connection->data_socket->fd = -1;
		read_from_stdin(connection);
		return;
	}


	/* This function works on already simplified file-scheme URI pre-chewed
	 * by transform_file_url(). By now, the function contains no hostname
	 * part anymore, possibly relative path is converted to an absolute one
	 * and uri->data is just the final path to file/dir we should try to
	 * show. */

	if (!init_string(&name)
	    || !add_uri_to_string(&name, connection->uri, URI_PATH)) {
		done_string(&name);
		abort_connection(connection, connection_state(S_OUT_OF_MEM));
		return;
	}

	decode_uri_string(&name);

	/* In Win32, file_is_dir seems to always return 0 if the name
	 * ends with a directory separator.  */
	if ((name.length > 0 && dir_sep(name.source[name.length - 1]))
	    || file_is_dir(name.source)) {
		/* In order for global history and directory listing to
		 * function properly the directory url must end with a
		 * directory separator. */
		if (name.source[0] && !dir_sep(name.source[name.length - 1])) {
			redirect_location = (unsigned char *)STRING_DIR_SEP;
			state = connection_state(S_OK);
		} else {
			state = list_directory(connection, name.source, &page);
			set_dir_content_type = 1;
		}

	} else {
		state = read_encoded_file(&name, &page);
		/* FIXME: If state is now S_ENCODE_ERROR we should try loading
		 * the file undecoded. --jonas */
	}

	done_string(&name);

	if (is_in_state(state, S_OK)) {
		struct cache_entry *cached;

		/* Try to add fragment data to the connection cache if either
		 * file reading or directory listing worked out ok. */
		cached = connection->cached = get_cache_entry(connection->uri);
		if (!connection->cached) {
			if (!redirect_location) done_string(&page);
			state = connection_state(S_OUT_OF_MEM);

		} else if (redirect_location) {
			if (!redirect_cache(cached, redirect_location, 1, 0))
				state = connection_state(S_OUT_OF_MEM);

		} else {
			add_fragment(cached, 0, page.source, page.length);
			connection->from += page.length;

			if (!cached->head && set_dir_content_type) {
				unsigned char *head;

				/* If the system charset somehow
				 * changes after the directory listing
				 * has been generated, it should be
				 * parsed with the original charset.  */
				head = straconcat((const unsigned char *)"\r\nContent-Type: text/html; charset=",
						  get_cp_mime_name(get_cp_index((const unsigned char *)"System")),
						  "\r\n", (unsigned char *) NULL);

				/* Not so gracefully handle failed memory
				 * allocation. */
				if (!head)
					state = connection_state(S_OUT_OF_MEM);

				/* Setup directory listing for viewing. */
				mem_free_set(&cached->head, head);
			}

			done_string(&page);
		}
	}

	abort_connection(connection, state);
}