mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-29 13:44:26 +00:00
parent
12c10dbcd1
commit
e8ea28d897
@ -635,7 +635,24 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
|
|||||||
** File interface **
|
** File interface **
|
||||||
********************/
|
********************/
|
||||||
|
|
||||||
bool poll(bool, Vfs::Vfs_handle::Context *) { return true; }
|
bool poll(bool trigger_io_response, Vfs::Vfs_handle::Context *context)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The connect file is considered readable when the socket is
|
||||||
|
* writeable (connected or error).
|
||||||
|
*/
|
||||||
|
|
||||||
|
using namespace Linux;
|
||||||
|
|
||||||
|
file f;
|
||||||
|
f.f_flags = 0;
|
||||||
|
if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLOUT_SET)) {
|
||||||
|
if (trigger_io_response)
|
||||||
|
_parent.trigger_io_response(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Lxip::ssize_t write(Lxip_vfs_file_handle &handle,
|
Lxip::ssize_t write(Lxip_vfs_file_handle &handle,
|
||||||
char const *src, Genode::size_t len,
|
char const *src, Genode::size_t len,
|
||||||
@ -657,12 +674,12 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
|
|||||||
addr->sin_addr.s_addr = get_addr(handle.content_buffer);
|
addr->sin_addr.s_addr = get_addr(handle.content_buffer);
|
||||||
addr->sin_family = AF_INET;
|
addr->sin_family = AF_INET;
|
||||||
|
|
||||||
_write_err = _sock.ops->connect(&_sock, (sockaddr *)addr, sizeof(addr_storage), 0);
|
_write_err = _sock.ops->connect(&_sock, (sockaddr *)addr, sizeof(addr_storage), O_NONBLOCK);
|
||||||
|
|
||||||
switch (_write_err) {
|
switch (_write_err) {
|
||||||
case Lxip::Io_result::LINUX_EINPROGRESS:
|
case Lxip::Io_result::LINUX_EINPROGRESS:
|
||||||
_connecting = true;
|
_connecting = true;
|
||||||
return -1;
|
return len;
|
||||||
|
|
||||||
case Lxip::Io_result::LINUX_EALREADY:
|
case Lxip::Io_result::LINUX_EALREADY:
|
||||||
return -1;
|
return -1;
|
||||||
@ -679,6 +696,7 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
if (_write_err != 0) return -1;
|
if (_write_err != 0) return -1;
|
||||||
|
_is_connected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,6 +709,29 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
|
|||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Lxip::ssize_t read(Lxip_vfs_file_handle &handle,
|
||||||
|
char *dst, Genode::size_t len,
|
||||||
|
file_size /* ignored */) override
|
||||||
|
{
|
||||||
|
int so_error = 0;
|
||||||
|
int opt_len = sizeof(so_error);
|
||||||
|
int res = sock_getsockopt(&_sock, SOL_SOCKET, SO_ERROR, (char*)&so_error, &opt_len);
|
||||||
|
|
||||||
|
if (res != 0) {
|
||||||
|
Genode::error("Vfs::Lxip_connect_file::read(): getsockopt() failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (so_error) {
|
||||||
|
case 0:
|
||||||
|
return Genode::snprintf(dst, len, "connected");
|
||||||
|
case Linux::ECONNREFUSED:
|
||||||
|
return Genode::snprintf(dst, len, "connection refused");
|
||||||
|
default:
|
||||||
|
return Genode::snprintf(dst, len, "unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,6 +114,8 @@ struct Socket_fs::Context : Libc::Plugin_context
|
|||||||
|
|
||||||
enum Proto { TCP, UDP };
|
enum Proto { TCP, UDP };
|
||||||
|
|
||||||
|
enum State { UNCONNECTED, ACCEPT_ONLY, CONNECTING, CONNECTED, CONNECT_ABORTED };
|
||||||
|
|
||||||
struct Inaccessible { }; /* exception */
|
struct Inaccessible { }; /* exception */
|
||||||
|
|
||||||
Absolute_path const path {
|
Absolute_path const path {
|
||||||
@ -141,7 +143,7 @@ struct Socket_fs::Context : Libc::Plugin_context
|
|||||||
|
|
||||||
Proto const _proto;
|
Proto const _proto;
|
||||||
|
|
||||||
bool _accept_only = false;
|
State _state { UNCONNECTED };
|
||||||
|
|
||||||
template <typename FUNC>
|
template <typename FUNC>
|
||||||
void _fd_apply(FUNC const &fn)
|
void _fd_apply(FUNC const &fn)
|
||||||
@ -196,7 +198,7 @@ struct Socket_fs::Context : Libc::Plugin_context
|
|||||||
}
|
}
|
||||||
|
|
||||||
int data_fd() { return _fd_for_type(Fd::DATA, O_RDWR); }
|
int data_fd() { return _fd_for_type(Fd::DATA, O_RDWR); }
|
||||||
int connect_fd() { return _fd_for_type(Fd::CONNECT, O_WRONLY); }
|
int connect_fd() { return _fd_for_type(Fd::CONNECT, O_RDWR); }
|
||||||
int bind_fd() { return _fd_for_type(Fd::BIND, O_WRONLY); }
|
int bind_fd() { return _fd_for_type(Fd::BIND, O_WRONLY); }
|
||||||
int listen_fd() { return _fd_for_type(Fd::LISTEN, O_WRONLY); }
|
int listen_fd() { return _fd_for_type(Fd::LISTEN, O_WRONLY); }
|
||||||
int accept_fd() { return _fd_for_type(Fd::ACCEPT, O_RDONLY); }
|
int accept_fd() { return _fd_for_type(Fd::ACCEPT, O_RDONLY); }
|
||||||
@ -204,16 +206,57 @@ struct Socket_fs::Context : Libc::Plugin_context
|
|||||||
int remote_fd() { return _fd_for_type(Fd::REMOTE, O_RDWR); }
|
int remote_fd() { return _fd_for_type(Fd::REMOTE, O_RDWR); }
|
||||||
|
|
||||||
/* request the appropriate fd to ensure the file is open */
|
/* request the appropriate fd to ensure the file is open */
|
||||||
bool data_read_ready() { data_fd(); return _fd_read_ready(Fd::DATA); }
|
bool connect_read_ready() { connect_fd(); return _fd_read_ready(Fd::CONNECT); }
|
||||||
bool accept_read_ready() { accept_fd(); return _fd_read_ready(Fd::ACCEPT); }
|
bool data_read_ready() { data_fd(); return _fd_read_ready(Fd::DATA); }
|
||||||
bool local_read_ready() { local_fd(); return _fd_read_ready(Fd::LOCAL); }
|
bool accept_read_ready() { accept_fd(); return _fd_read_ready(Fd::ACCEPT); }
|
||||||
bool remote_read_ready() { remote_fd(); return _fd_read_ready(Fd::REMOTE); }
|
bool local_read_ready() { local_fd(); return _fd_read_ready(Fd::LOCAL); }
|
||||||
|
bool remote_read_ready() { remote_fd(); return _fd_read_ready(Fd::REMOTE); }
|
||||||
|
|
||||||
void accept_only() { _accept_only = true; }
|
void state(State state) { _state = state; }
|
||||||
|
State state() const { return _state; }
|
||||||
|
|
||||||
bool read_ready()
|
bool read_ready()
|
||||||
{
|
{
|
||||||
return _accept_only ? accept_read_ready() : data_read_ready();
|
return (_state == ACCEPT_ONLY) ? accept_read_ready() : data_read_ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_ready()
|
||||||
|
{
|
||||||
|
if (_state == CONNECTING)
|
||||||
|
return connect_read_ready();
|
||||||
|
|
||||||
|
/* XXX ask if "data" is writeable */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the connect status from the connect file and return 0 if connected
|
||||||
|
* or -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int read_connect_status()
|
||||||
|
{
|
||||||
|
char connect_status[32] = { 0 };
|
||||||
|
ssize_t connect_status_len;
|
||||||
|
|
||||||
|
connect_status_len = read(connect_fd(), connect_status,
|
||||||
|
sizeof(connect_status));
|
||||||
|
|
||||||
|
if (connect_status_len <= 0) {
|
||||||
|
Genode::error("socket_fs: reading from the connect file failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(connect_status, "connected") == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (strcmp(connect_status, "connection refused") == 0)
|
||||||
|
return Errno(ECONNREFUSED);
|
||||||
|
|
||||||
|
if (strcmp(connect_status, "not connected") == 0)
|
||||||
|
return Errno(ENOTCONN);
|
||||||
|
|
||||||
|
Genode::error("socket_fs: unhandled connection state");
|
||||||
|
return Errno(ECONNREFUSED);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -566,23 +609,81 @@ extern "C" int socket_fs_connect(int libc_fd, sockaddr const *addr, socklen_t ad
|
|||||||
return Errno(EAFNOSUPPORT);
|
return Errno(EAFNOSUPPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO EISCONN */
|
switch (context->state()) {
|
||||||
/* TODO ECONNREFUSED */
|
case Context::UNCONNECTED:
|
||||||
/* TODO maybe EALREADY, EINPROGRESS, ETIMEDOUT */
|
{
|
||||||
|
Sockaddr_string addr_string;
|
||||||
|
try {
|
||||||
|
addr_string = Sockaddr_string(host_string(*(sockaddr_in const *)addr),
|
||||||
|
port_string(*(sockaddr_in const *)addr));
|
||||||
|
}
|
||||||
|
catch (Address_conversion_failed) { return Errno(EINVAL); }
|
||||||
|
|
||||||
Sockaddr_string addr_string;
|
context->state(Context::CONNECTING);
|
||||||
try {
|
|
||||||
addr_string = Sockaddr_string(host_string(*(sockaddr_in const *)addr),
|
int const len = strlen(addr_string.base());
|
||||||
port_string(*(sockaddr_in const *)addr));
|
int const n = write(context->connect_fd(), addr_string.base(), len);
|
||||||
|
|
||||||
|
if (n != len) return Errno(ECONNREFUSED);
|
||||||
|
|
||||||
|
if (context->fd_flags() & O_NONBLOCK)
|
||||||
|
return Errno(EINPROGRESS);
|
||||||
|
|
||||||
|
/* block until socket is ready for writing */
|
||||||
|
|
||||||
|
fd_set writefds;
|
||||||
|
FD_ZERO(&writefds);
|
||||||
|
FD_SET(libc_fd, &writefds);
|
||||||
|
|
||||||
|
enum { CONNECT_TIMEOUT_S = 10 };
|
||||||
|
struct timeval timeout {CONNECT_TIMEOUT_S, 0};
|
||||||
|
int res = select(libc_fd + 1, NULL, &writefds, NULL, &timeout);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
/* errno has been set by select() */
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
context->state(Context::CONNECT_ABORTED);
|
||||||
|
return Errno(ETIMEDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int connect_status = context->read_connect_status();
|
||||||
|
|
||||||
|
if (connect_status == 0)
|
||||||
|
context->state(Context::CONNECTED);
|
||||||
|
else
|
||||||
|
context->state(Context::CONNECT_ABORTED);
|
||||||
|
|
||||||
|
/* errno has been set by context->read_connect_status() */
|
||||||
|
return connect_status;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Context::ACCEPT_ONLY:
|
||||||
|
return Errno(EINVAL);
|
||||||
|
case Context::CONNECTING:
|
||||||
|
{
|
||||||
|
if (!context->connect_read_ready())
|
||||||
|
return Errno(EALREADY);
|
||||||
|
|
||||||
|
int connect_status = context->read_connect_status();
|
||||||
|
|
||||||
|
if (connect_status == 0)
|
||||||
|
context->state(Context::CONNECTED);
|
||||||
|
else
|
||||||
|
context->state(Context::CONNECT_ABORTED);
|
||||||
|
|
||||||
|
/* errno was set by context->read_connect_status() */
|
||||||
|
return connect_status;
|
||||||
|
}
|
||||||
|
case Context::CONNECTED:
|
||||||
|
return Errno(EISCONN);
|
||||||
|
case Context::CONNECT_ABORTED:
|
||||||
|
return Errno(ECONNABORTED);
|
||||||
}
|
}
|
||||||
catch (Address_conversion_failed) { return Errno(EINVAL); }
|
|
||||||
|
|
||||||
int const len = strlen(addr_string.base());
|
return Errno(ECONNREFUSED);
|
||||||
int const n = write(context->connect_fd(), addr_string.base(), len);
|
|
||||||
if (n != len) return Errno(ECONNREFUSED);
|
|
||||||
|
|
||||||
/* sync to block for write completion */
|
|
||||||
return fsync(context->connect_fd());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -599,7 +700,7 @@ extern "C" int socket_fs_listen(int libc_fd, int backlog)
|
|||||||
int const n = write(context->listen_fd(), buf, len);
|
int const n = write(context->listen_fd(), buf, len);
|
||||||
if (n != len) return Errno(EOPNOTSUPP);
|
if (n != len) return Errno(EOPNOTSUPP);
|
||||||
|
|
||||||
context->accept_only();
|
context->state(Context::ACCEPT_ONLY);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,6 +833,21 @@ extern "C" int socket_fs_getsockopt(int libc_fd, int level, int optname,
|
|||||||
*(int *)optval = 1;
|
*(int *)optval = 1;
|
||||||
return 0;
|
return 0;
|
||||||
case SO_ERROR:
|
case SO_ERROR:
|
||||||
|
if (context->state() == Context::CONNECTING) {
|
||||||
|
|
||||||
|
int connect_status = context->read_connect_status();
|
||||||
|
|
||||||
|
if (connect_status == 0) {
|
||||||
|
*(int*)optval = 0;
|
||||||
|
context->state(Context::CONNECTED);
|
||||||
|
} else {
|
||||||
|
*(int*)optval = errno;
|
||||||
|
context->state(Context::CONNECT_ABORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* not yet implemented - but return true */
|
/* not yet implemented - but return true */
|
||||||
*(int *)optval = 0;
|
*(int *)optval = 0;
|
||||||
return 0;
|
return 0;
|
||||||
@ -948,10 +1064,14 @@ int Socket_fs::Plugin::select(int nfds,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (FD_ISSET(fd, &in_writefds)) {
|
if (FD_ISSET(fd, &in_writefds)) {
|
||||||
if (true /* XXX ask if "data" is writeable */) {
|
try {
|
||||||
FD_SET(fd, writefds);
|
Socket_fs::Context *context = dynamic_cast<Socket_fs::Context *>(fdo->context);
|
||||||
++nready;
|
|
||||||
}
|
if (context->write_ready()) {
|
||||||
|
FD_SET(fd, writefds);
|
||||||
|
++nready;
|
||||||
|
}
|
||||||
|
} catch (Socket_fs::Context::Inaccessible) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX exceptfds not supported */
|
/* XXX exceptfds not supported */
|
||||||
|
@ -827,9 +827,11 @@ class Lwip::Udp_socket_dir final :
|
|||||||
|
|
||||||
case Lwip_file_handle::CONNECT: {
|
case Lwip_file_handle::CONNECT: {
|
||||||
/* check if the PCB was connected */
|
/* check if the PCB was connected */
|
||||||
if (ip_addr_isany(&_pcb->remote_ip))
|
if (!ip_addr_isany(&_pcb->remote_ip))
|
||||||
return Read_result::READ_OK;
|
out_count = Genode::snprintf(dst, count, "connected");
|
||||||
/* otherwise fallthru to REMOTE*/
|
else
|
||||||
|
out_count = Genode::snprintf(dst, count, "not connected");
|
||||||
|
return Read_result::READ_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Lwip_file_handle::REMOTE: {
|
case Lwip_file_handle::REMOTE: {
|
||||||
@ -1162,7 +1164,11 @@ class Lwip::Tcp_socket_dir final :
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Lwip_file_handle::CONNECT:
|
case Lwip_file_handle::CONNECT:
|
||||||
return !ip_addr_isany(&_pcb->remote_ip);
|
/*
|
||||||
|
* The connect file is considered readable when the socket is
|
||||||
|
* writeable (connected or error).
|
||||||
|
*/
|
||||||
|
return ((state == READY) || (state == CLOSED));
|
||||||
|
|
||||||
case Lwip_file_handle::LOCATION:
|
case Lwip_file_handle::LOCATION:
|
||||||
case Lwip_file_handle::LOCAL:
|
case Lwip_file_handle::LOCAL:
|
||||||
@ -1303,6 +1309,15 @@ class Lwip::Tcp_socket_dir final :
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Lwip_file_handle::CONNECT:
|
case Lwip_file_handle::CONNECT:
|
||||||
|
switch (state) {
|
||||||
|
case READY:
|
||||||
|
out_count = Genode::snprintf(dst, count, "connected");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out_count = Genode::snprintf(dst, count, "connection refused");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return Read_result::READ_OK;
|
||||||
case Lwip_file_handle::LISTEN:
|
case Lwip_file_handle::LISTEN:
|
||||||
case Lwip_file_handle::INVALID: break;
|
case Lwip_file_handle::INVALID: break;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user