コード例 #1
0
ファイル: socket.c プロジェクト: nabetaro/elinks
/* Select handler which is set for the socket descriptor when connect() has
 * indicated (via errno) that it is in progress. On completion this handler gets
 * called. */
static void
connected(struct socket *socket)
{
	int err = 0;
	struct connection_state state = connection_state(0);
	socklen_t len = sizeof(err);

	assertm(socket->connect_info != NULL, "Lost connect_info!");
	if_assert_failed return;

	if (getsockopt(socket->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == 0) {
		/* Why does EMX return so large values? */
		if (err >= 10000) err -= 10000;
		if (err != 0)
			state = connection_state_for_errno(err);
		else
			state = connection_state(0);
	} else {
		/* getsockopt() failed */
		if (errno != 0)
			state = connection_state_for_errno(errno);
		else
			state = connection_state(S_STATE);
	}

	if (!is_in_state(state, 0)) {
		/* There are maybe still some more candidates. */
		connect_socket(socket, state);
		return;
	}

	complete_connect_socket(socket, NULL, NULL);
}
コード例 #2
0
ファイル: file.c プロジェクト: rkd77/elinks-tv
/* Returns a connection state. S_OK if all is well. */
static inline struct connection_state
list_directory(struct connection *conn, unsigned char *dirpath,
	       struct string *page)
{
	int show_hidden_files = get_opt_bool((const unsigned char *)"protocol.file.show_hidden_files",
	                                     NULL);
	struct directory_entry *entries;
	struct connection_state state;

	errno = 0;
	entries = get_directory_entries(dirpath, show_hidden_files);
	if (!entries) {
		if (errno) return connection_state_for_errno(errno);
		return connection_state(S_OUT_OF_MEM);
	}

	state = init_directory_listing(page, conn->uri);
	if (!is_in_state(state, S_OK))
		return connection_state(S_OUT_OF_MEM);

	add_dir_entries(entries, dirpath, page);

	if (!add_to_string(page, (const unsigned char *)"</pre>\n<hr/>\n</body>\n</html>\n")) {
		done_string(page);
		return connection_state(S_OUT_OF_MEM);
	}

	return connection_state(S_OK);
}
コード例 #3
0
ファイル: fsp.c プロジェクト: Efreak/elinks
static void
fsp_directory(FSP_SESSION *ses, struct uri *uri)
{
	struct string buf;
	FSP_DIR *dir;
	unsigned char *data = get_uri_string(uri, URI_DATA);
	unsigned char dircolor[8] = "";

	if (!data)
		fsp_error(connection_state(S_OUT_OF_MEM));
	decode_uri(data);
	if (!is_in_state(init_directory_listing(&buf, uri), S_OK))
		fsp_error(connection_state(S_OUT_OF_MEM));

	dir = fsp_opendir(ses, data);
	if (!dir) fsp_error(connection_state_for_errno(errno));

	fprintf(stderr, "text/html");
	fclose(stderr);

	puts(buf.source);

	if (get_opt_bool("document.browse.links.color_dirs", NULL)) {
		color_to_string(get_opt_color("document.colors.dirs", NULL),
				dircolor);
	}

	sort_and_display_entries(dir, dircolor);
	fsp_closedir(dir);
	puts("</pre><hr/></body></html>");
	fsp_close_session(ses);
	exit(0);
}
コード例 #4
0
ファイル: smb2.c プロジェクト: Efreak/elinks
static void
smb_got_data(struct socket *socket, struct read_buffer *rb)
{
	int len = rb->length;
	struct connection *conn = socket->conn;

	if (len < 0) {
		abort_connection(conn, connection_state_for_errno(errno));
		return;
	}

	if (!len) {
		abort_connection(conn, connection_state(S_OK));
		return;
	}

	socket->state = SOCKET_END_ONCLOSE;
	conn->received += len;
	if (add_fragment(conn->cached, conn->from, rb->data, len) == 1)
		conn->tries = 0;
	conn->from += len;
	kill_buffer_data(rb, len);

	read_from_socket(socket, rb, connection_state(S_TRANS), smb_got_data);
}
コード例 #5
0
ファイル: post.c プロジェクト: rkd77/elinks-tv
/** @return -2 if no data was read but the caller should retry;
 * -1 if an error occurred and *@a error was set; 0 at end of data;
 * a positive number if that many bytes were read.
 *
 * @relates http_post */
static int
read_http_post_fd(struct http_post *http_post,
		  unsigned char buffer[], int max,
		  struct connection_state *error)
{
	const struct http_post_file *const file
		= &http_post->files[http_post->file_index];
	int ret;

	/* safe_read() would set errno = EBADF anyway, but check this
	 * explicitly to make any such bugs easier to detect.  */
	assert(http_post->post_fd >= 0);
	if_assert_failed { *error = connection_state(S_INTERNAL); return -1; }

	ret = safe_read(http_post->post_fd, buffer, max);
	if (ret <= 0) {
		const int errno_from_read = errno;

		close(http_post->post_fd);
		http_post->post_fd = -1;
		http_post->file_index++;
		/* http_post->file_read is used below so don't clear it here.
		 * It will be cleared when the next file is opened.  */

		if (ret == -1) {
			*error = connection_state_for_errno(errno_from_read);
			return -1;
		} else if (http_post->file_read != file->size) {
			/* ELinks already sent a Content-Length header
			 * based on the size of this file, but the
			 * file has since been shrunk.  Abort the
			 * connection because ELinks can no longer get
			 * enough data to fill the Content-Length.
			 * (Well, it could pad with zeroes, but that
			 * would be just weird.)  */
			*error = connection_state(S_HTTP_UPLOAD_RESIZED);
			return -1;
		} else {
			/* The upload file ended but there may still
			 * be more data in uri.post.  If not,
			 * read_http_post_inline() will return 0 to
			 * indicate the final end of file.  */
			return -2;
		}
	}

	http_post->file_read += ret;
	if (http_post->file_read > file->size) {
		/* ELinks already sent a Content-Length header based
		 * on the size of this file, but the file has since
		 * been extended.  Abort the connection because ELinks
		 * can no longer fit the entire file in the original
		 * Content-Length.  */
		*error = connection_state(S_HTTP_UPLOAD_RESIZED);
		return -1;
	}

	return ret;
}
コード例 #6
0
ファイル: smb2.c プロジェクト: Efreak/elinks
static void
smb_got_error(struct socket *socket, struct read_buffer *rb)
{
	int len = rb->length;
	struct connection *conn = socket->conn;
	struct connection_state error;

	if (len < 0) {
		abort_connection(conn, connection_state_for_errno(errno));
		return;
	}

	/* There should be free space in the buffer, because
	 * @alloc_read_buffer allocated several kibibytes, and the
	 * child process wrote only an integer and a newline to the
	 * pipe.  */
	assert(rb->freespace >= 1);
	if_assert_failed {
		abort_connection(conn, connection_state(S_INTERNAL));
		return;
	}
	rb->data[len] = '\0';
	switch (rb->data[0]) {
	case 'S':
		error = connection_state_for_errno(atoi(rb->data + 1));
		break;
	case 'I':
		error = connection_state(atoi(rb->data + 1));
		break;
	default:
		ERROR("malformed error code: %s", rb->data);
		error = connection_state(S_INTERNAL);
		break;
	}
	kill_buffer_data(rb, len);

	if (is_system_error(error) && error.syserr == EACCES)
		prompt_username_pw(conn);
	else
		abort_connection(conn, error);
}
コード例 #7
0
ファイル: post.c プロジェクト: rkd77/elinks-tv
/** @return -2 if no data was read but the caller should retry;
 * -1 if an error occurred and *@a error was set; 0 at end of data;
 * a positive number if that many bytes were read.
 *
 * @relates http_post */
static int
read_http_post_inline(struct http_post *http_post,
		      unsigned char buffer[], int max,
		      struct connection_state *error)
{
	const unsigned char *post = http_post->post_data;
	const unsigned char *end = (const unsigned char *)strchr((char *)post, FILE_CHAR);
	int total = 0;

	assert(http_post->post_fd < 0);
	if_assert_failed { *error = connection_state(S_INTERNAL); return -1; }

	if (!end)
		end = (const unsigned char *)strchr((char *)post, '\0');

	while (post < end && total < max) {
		int h1, h2;

		h1 = unhx(post[0]);
		assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d (%d/%c)", h1, post[0], post[0]);
		if_assert_failed h1 = 0;

		h2 = unhx(post[1]);
		assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d (%d/%c)", h2, post[1], post[1]);
		if_assert_failed h2 = 0;

		buffer[total++] = (h1<<4) + h2;
		post += 2;
	}
	if (post != end || *end != FILE_CHAR) {
		http_post->post_data = post;
		return total;
	}

	http_post->file_read = 0;
	end = (const unsigned char *)strchr((char *)(post + 1), FILE_CHAR);
	assert(end);
	http_post->post_fd = open((const char *)http_post->files[http_post->file_index].name,
				  O_RDONLY);
	/* Be careful not to change errno here.  */
	if (http_post->post_fd < 0) {
		http_post->post_data = post;
		if (total > 0)
			return total; /* retry the open on the next call */
		else {
			*error = connection_state_for_errno(errno);
			return -1;
		}
	}
	http_post->post_data = end + 1;
	return total ? total : -2;
}
コード例 #8
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));
}
コード例 #9
0
ファイル: smb2.c プロジェクト: Efreak/elinks
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);
	}
}
コード例 #10
0
ファイル: smb2.c プロジェクト: Efreak/elinks
static void
do_smb(struct connection *conn)
{
	struct uri *uri = conn->uri;
	struct auth_entry *auth = find_auth(uri);
	struct string string;
	unsigned char *url;
	int dir;

	if ((uri->userlen && uri->passwordlen) || !auth) {
		url = get_uri_string(uri, URI_BASE);
	} else {
		unsigned char *uri_string = get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA);

		if (!uri_string || !init_string(&string)) {
			smb_error(connection_state(S_OUT_OF_MEM));
		}
		/* Must URI-encode the username and password to avoid
		 * ambiguity if they contain "/:@" characters.
		 * Libsmbclient then decodes them again, and the
		 * server gets them as they were in auth->user and
		 * auth->password, i.e. as the user typed them in the
		 * auth dialog.  This implies that, if the username or
		 * password contains some characters or bytes that the
		 * user cannot directly type, then she cannot enter
		 * them.  If that becomes an actual problem, it should
		 * be fixed in the auth dialog, e.g. by providing a
		 * hexadecimal input mode.  */
		add_to_string(&string, "smb://");
		encode_uri_string(&string, auth->user, -1, 1);
		add_char_to_string(&string, ':');
		encode_uri_string(&string, auth->password, -1, 1);
		add_char_to_string(&string, '@');
		add_to_string(&string, uri_string);
		url = string.source;
	}

	if (!url) {
		smb_error(connection_state(S_OUT_OF_MEM));
	}
	if (smbc_init(smb_auth, 0)) {
		smb_error(connection_state_for_errno(errno));
	};


	dir = smbc_opendir(url);
	if (dir >= 0) {
		struct string prefix;

		init_string(&prefix);
		add_to_string(&prefix, url);
		add_char_to_string(&prefix, '/');
		smb_directory(dir, &prefix, conn->uri);
		done_string(&prefix);
	} else {
		const int errno_from_opendir = errno;
		char buf[READ_SIZE];
		struct stat sb;
		int r, res, fdout;
		int file = smbc_open(url, O_RDONLY, 0);

		if (file < 0) {
			/* If we're opening the list of shares without
			 * proper authentication, then smbc_opendir
			 * fails with EACCES and smbc_open fails with
			 * ENOENT.  In this case, return the EACCES so
			 * that the parent ELinks process will prompt
			 * for credentials.  */
			if (errno == ENOENT && errno_from_opendir == EACCES)
				errno = errno_from_opendir;
			smb_error(connection_state_for_errno(errno));
		}

		res = smbc_fstat(file, &sb);
		if (res) {
			smb_error(connection_state_for_errno(res));
		}
		/* filesize */
		fprintf(header_out, "%" OFF_PRINT_FORMAT,
			(off_print_T) sb.st_size);
		fclose(header_out);

		fdout = fileno(data_out);
		while ((r = smbc_read(file, buf, READ_SIZE)) > 0) {
			if (safe_write(fdout, buf, r) <= 0)
					break;
		}
		smbc_close(file);
		exit(0);
	}
}
コード例 #11
0
ファイル: socket.c プロジェクト: nabetaro/elinks
static void
read_select(struct socket *socket)
{
	struct read_buffer *rb = socket->read_buffer;
	ssize_t rd;

	assertm(rb != NULL, "read socket has no buffer");
	if_assert_failed {
		socket->ops->done(socket, connection_state(S_INTERNAL));
		return;
	}

	/* We are making some progress, therefore reset the timeout; we do this
	 * for read_select() to avoid that the periodic calls to user handlers
	 * has to do it. */
	socket->ops->set_timeout(socket, connection_state(0));

	if (!socket->duplex)
		clear_handlers(socket->fd);

	if (!rb->freespace) {
		int size = RD_SIZE(rb, rb->length);

		rb = mem_realloc(rb, size);
		if (!rb) {
			socket->ops->done(socket, connection_state(S_OUT_OF_MEM));
			return;
		}
		rb->freespace = size - sizeof(*rb) - rb->length;
		assert(rb->freespace > 0);
		socket->read_buffer = rb;
	}

#ifdef CONFIG_SSL
	if (socket->ssl) {
		rd = ssl_read(socket, rb->data + rb->length, rb->freespace);
	} else
#endif
	{
		rd = generic_read(socket, rb->data + rb->length, rb->freespace);
	}

	switch (rd) {
#ifdef CONFIG_SSL
	case SOCKET_SSL_WANT_READ:
		read_from_socket(socket, rb, connection_state(S_TRANS), rb->done);
		break;
#endif
	case SOCKET_CANT_READ:
		if (socket->state != SOCKET_RETRY_ONCLOSE) {
			socket->state = SOCKET_CLOSED;
			rb->done(socket, rb);
			break;
		}

		socket->ops->retry(socket, connection_state(S_CANT_READ));
		break;

	case SOCKET_SYSCALL_ERROR:
		socket->ops->retry(socket, connection_state_for_errno(errno));
		break;

	case SOCKET_INTERNAL_ERROR:
		/* The global errno variable is used for passing
		 * internal connection_state error value. */
		socket->ops->done(socket, connection_state(errno));
		break;

	default:
		debug_transfer_log(rb->data + rb->length, rd);

		rb->length += rd;
		rb->freespace -= rd;
		assert(rb->freespace >= 0);

		rb->done(socket, rb);
	}
}
コード例 #12
0
ファイル: socket.c プロジェクト: nabetaro/elinks
static void
write_select(struct socket *socket)
{
	struct write_buffer *wb = socket->write_buffer;
	int wr;

	assertm(wb != NULL, "write socket has no buffer");
	if_assert_failed {
		socket->ops->done(socket, connection_state(S_INTERNAL));
		return;
	}

	/* We are making some progress, therefore reset the timeout; ie.  when
	 * uploading large files the time needed for all the data to be sent can
	 * easily exceed the timeout. */
	socket->ops->set_timeout(socket, connection_state(0));

#if 0
	printf("ws: %d\n",wb->length-wb->pos);
	for (wr = wb->pos; wr < wb->length; wr++) printf("%c", wb->data[wr]);
	printf("-\n");
#endif

#ifdef CONFIG_SSL
	if (socket->ssl) {
		wr = ssl_write(socket, wb->data + wb->pos, wb->length - wb->pos);
	} else
#endif
	{
		assert(wb->length - wb->pos > 0);
		wr = generic_write(socket, wb->data + wb->pos, wb->length - wb->pos);
	}

	switch (wr) {
	case SOCKET_CANT_WRITE:
		socket->ops->retry(socket, connection_state(S_CANT_WRITE));
		break;

	case SOCKET_SYSCALL_ERROR:
		socket->ops->retry(socket, connection_state_for_errno(errno));
		break;

	case SOCKET_INTERNAL_ERROR:
		/* The global errno variable is used for passing
		 * internal connection_state error value. */
		socket->ops->done(socket, connection_state(errno));
		break;

	default:
		if (wr < 0) break;

		/*printf("wr: %d\n", wr);*/
		wb->pos += wr;

		if (wb->pos == wb->length) {
			socket_write_T done = wb->done;

			if (!socket->duplex) {
				clear_handlers(socket->fd);

			} else {
				select_handler_T read_handler;
				select_handler_T error_handler;

				read_handler  = get_handler(socket->fd, SELECT_HANDLER_READ);
				error_handler = read_handler
					      ? (select_handler_T) exception
					      : NULL;

				set_handlers(socket->fd, read_handler, NULL,
					     error_handler, socket);
			}

			mem_free_set(&socket->write_buffer, NULL);
			done(socket);
		}
	}
}
コード例 #13
0
ファイル: socket.c プロジェクト: nabetaro/elinks
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);
}
コード例 #14
0
ファイル: socket.c プロジェクト: nabetaro/elinks
/* 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;
}
コード例 #15
0
ファイル: peerconnect.c プロジェクト: Efreak/elinks
/* 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);
}
コード例 #16
0
ファイル: post.c プロジェクト: rkd77/elinks-tv
/** Prepare to read POST data from a URI and possibly to upload files.
 *
 * @param http_post
 *   Must have been initialized with init_http_post().
 * @param[in] post_data
 *   The body of the POST request as formatted by get_form_uri().
 *   However, unlike uri.post, @a post_data must not contain any
 *   Content-Type.  The caller must ensure that the @a post_data
 *   pointer remains valid until done_http_post().
 * @param[out] error
 *   If the function fails, it writes the error state here so that
 *   the caller can pass that on to abort_connection().  If the
 *   function succeeds, the value of *@a error is undefined.
 *
 * This function does not parse the Content-Type from uri.post; the
 * caller must do that.  This is because in local CGI, the child
 * process handles the Content-Type (saving it to an environment
 * variable before exec) but the parent process handles the body of
 * the request (feeding it to the child process via a pipe).
 *
 * @return nonzero on success, zero on error.
 *
 * @relates http_post */
int
open_http_post(struct http_post *http_post, const unsigned char *post_data,
	       struct connection_state *error)
{
	off_t size = 0;
	size_t length = strlen((const char *)post_data);
	const unsigned char *end = post_data;

	done_http_post(http_post);
	http_post->post_data = end;

	while (1) {
		struct stat sb;
		const unsigned char *begin;
		int res;
		struct http_post_file *new_files;
		unsigned char *filename;

		begin = (const unsigned char *)strchr((char *)end, FILE_CHAR);
		if (!begin) break;
		end = (const unsigned char *)strchr((char *)(begin + 1), FILE_CHAR);
		if (!end) break;
		filename = memacpy(begin + 1, end - begin - 1); /* adds '\0' */
		if (!filename) {
			done_http_post(http_post);
			*error = connection_state(S_OUT_OF_MEM);
			return 0;
		}
		decode_uri(filename);
		res = stat((const char *)filename, &sb);
		if (res) {
			*error = connection_state_for_errno(errno);
			done_http_post(http_post);
			return 0;
		}

		/* This use of mem_realloc() in a loop consumes O(n^2)
		 * time but how many files are you really going to
		 * upload in one request?  */
		new_files = (struct http_post_file *)mem_realloc(http_post->files,
					(http_post->file_count + 1)
					* sizeof(*new_files));
		if (new_files == NULL) {
			mem_free(filename);
			done_http_post(http_post);
			*error = connection_state(S_OUT_OF_MEM);
			return 0;
		}
		http_post->files = new_files;
		new_files[http_post->file_count].name = filename;
		new_files[http_post->file_count].size = sb.st_size;
		http_post->file_count++;

		size += sb.st_size;
		length -= (end - begin + 1);
		end++;
	}
	size += (length / 2);
	http_post->total_upload_length = size;

	return 1;
}
コード例 #17
0
ファイル: fsp.c プロジェクト: Efreak/elinks
static void
do_fsp(struct connection *conn)
{
	FSP_SESSION *ses;
	struct stat sb;
	struct uri *uri = conn->uri;
	struct auth_entry *auth;
	unsigned char *host = get_uri_string(uri, URI_HOST);
	unsigned char *data = get_uri_string(uri, URI_DATA);
	unsigned short port = (unsigned short)get_uri_port(uri);
	unsigned char *password = NULL;

	decode_uri(data);
	if (uri->passwordlen) {
		password = get_uri_string(uri, URI_PASSWORD);
	} else {
		auth = find_auth(uri);
		if (auth) password = auth->password;
	}

	/* fsp_open_session may not set errno if getaddrinfo fails
	 * https://sourceforge.net/tracker/index.php?func=detail&aid=2036798&group_id=93841&atid=605738
	 * Try to detect this bug and use an ELinks-specific error
	 * code instead, so that we can display a message anyway.  */
	errno = 0;
	ses = fsp_open_session(host, port, password);
	if (!ses) {
		if (errno)
			fsp_error(connection_state_for_errno(errno));
		else
			fsp_error(connection_state(S_FSP_OPEN_SESSION_UNKN));
	}

	/* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS
	 * https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738
	 * If ELinks and fsplib are using different values of
	 * _FILE_OFFSET_BITS, then they get different definitions of
	 * struct stat, and the st_size stored by fsp_stat is
	 * typically not the same as the st_size read by ELinks.
	 * Fortunately, st_mode seems to have the same offset and size
	 * in both versions of struct stat.
	 *
	 * If all the bytes used by the 32-bit st_size are also used
	 * by the 64-bit st_size, then ELinks may be able to guess
	 * which ones they are, because the current version 2 of FSP
	 * supports only 32-bit file sizes in protocol packets.  Begin
	 * by filling struct stat with 0xAA so that it's easier to
	 * detect which bytes fsp_stat has left unchanged.  (Only
	 * sb.st_size really needs to be filled, but filling the rest
	 * too helps viewing the data with a debugger.)  */
	memset(&sb, 0xAA, sizeof(sb));
	if (fsp_stat(ses, data, &sb)) fsp_error(connection_state_for_errno(errno));

	if (S_ISDIR(sb.st_mode)) {
		fsp_directory(ses, uri);
	} else { /* regular file */
		char buf[READ_SIZE];
		FSP_FILE *file = fsp_fopen(ses, data, "r");
		int r;

		if (!file) {
			fsp_error(connection_state_for_errno(errno));
		}

#if SIZEOF_OFF_T >= 8
		if (sb.st_size < 0 || sb.st_size > 0xFFFFFFFF) {
			/* Probably a _FILE_OFFSET_BITS mismatch as
			 * described above.  Try to detect which half
			 * of st_size is the real size.  This may
			 * depend on the endianness of the processor
			 * and on the padding in struct stat.  */
			if ((sb.st_size & 0xFFFFFFFF00000000ULL) == 0xAAAAAAAA00000000ULL)
				sb.st_size = sb.st_size & 0xFFFFFFFF;
			else if ((sb.st_size & 0xFFFFFFFF) == 0xAAAAAAAA)
				sb.st_size = (sb.st_size >> 32) & 0xFFFFFFFF;
			else	/* Can't figure it out. */
				sb.st_size = 1;
		}
#endif

		/* Send filesize */
		fprintf(stderr, "%" OFF_PRINT_FORMAT "\n",
			(off_print_T) sb.st_size);
		fclose(stderr);

		while ((r = fsp_fread(buf, 1, READ_SIZE, file)) > 0) {
			int off = 0;

			while (r) {
				int w = safe_write(STDOUT_FILENO, buf + off, r);

				if (w == -1) goto out;
				off += w;
				r -= w;
			}
		}
out:
		fsp_fclose(file);
		fsp_close_session(ses);
		exit(0);
	}