Commit a6560459 authored by Niels Möller's avatar Niels Möller
Browse files

(class socks_channel): Replaces class

socks_connection, and inherits channel_forward.
(class socks_continuation): Deleted.
(do_socks_continuation, make_socks_continuation): Deleted.
(class socks_exception_handler): Deleted.
(do_exc_socks_handler, make_socks_exception_handler): Deleted.
(do_socks_channel_event): New function, in particular handling
CHANNEL_EVENT_CONFIRM and CHANNEL_EVENT_DENY.

Rev: src/socks.c:1.5.2.5
parent dc29b85e
......@@ -115,29 +115,25 @@ static const uint8_t ip4_noaddr[4] = {0,0,0,0};
/* Forward declarations */
static void
socks_start_write(struct socks_connection *self);
socks_start_write(struct socks_channel *self);
static void
socks_stop_write(struct socks_connection *self);
socks_stop_write(struct socks_channel *self);
static void
socks_start_read(struct socks_connection *self);
socks_start_read(struct socks_channel *self);
static void
socks_stop_read(struct socks_connection *self);
socks_stop_read(struct socks_channel *self);
/* GABA:
(class
(name socks_connection)
(super resource)
(name socks_channel)
(super channel_forward)
(vars
(connection object ssh_connection)
; We use the channel's read and write buffers
; FIXME: It would be cleaner to use our own buffers,
; and not allocate the channel until we try opening it.
(channel object channel_forward)
; We use the channel's read and write buffers for the socks
; handshake.
; The write position (i.e. the amount of data) in the read buffer
(pos . uint32_t)
......@@ -152,73 +148,32 @@ socks_stop_read(struct socks_connection *self);
*/
static void
do_kill_socks_connection(struct resource *s)
{
CAST(socks_connection, self, s);
if (self->super.alive)
{
trace("do_kill_socks_connection\n");
self->super.alive = 0;
if (self->channel)
KILL_RESOURCE(&self->channel->super.super);
}
}
static struct socks_connection *
make_socks_connection(struct ssh_connection *connection,
struct listen_value *lv)
{
NEW(socks_connection, self);
init_resource(&self->super, do_kill_socks_connection);
self->connection = connection;
self->channel = make_channel_forward(lv->fd, TCPIP_WINDOW_SIZE);
/* Worst-case margin, for any buffered reply when we take the channel
into use. */
self->channel->super.rec_window_size -= SOCKS_MAX_SIZE;
self->pos = 0;
self->length = 3;
self->peer = lv->peer;
self->state = SOCKS_VERSION_HEADER;
self->length = SOCKS_HEADER_SIZE;
self->version = 0;
self->target = NULL;
return self;
}
static void
socks_close(struct socks_connection *self)
socks_close(struct socks_channel *self)
{
self->state = SOCKS_CLOSE;
if (self->channel->write.state->length)
global_oop_source->cancel_fd(global_oop_source, self->channel->read.fd, OOP_READ);
if (self->super.write.state->length)
global_oop_source->cancel_fd(global_oop_source, self->super.read.fd, OOP_READ);
else
KILL_RESOURCE(&self->super);
KILL_RESOURCE(&self->super.super.super);
}
static void
socks_fail(struct socks_connection *self)
socks_fail(struct socks_channel *self)
{
KILL_RESOURCE(&self->super);
KILL_RESOURCE(&self->super.super.super);
}
static void
socks_write(struct socks_connection *self, struct lsh_string *data)
socks_write(struct socks_channel *self, struct lsh_string *data)
{
uint32_t done = ssh_write_data(self->channel->write.state, self->channel->write.fd,
uint32_t done = ssh_write_data(self->super.write.state, self->super.write.fd,
0, STRING_LD(data));
lsh_string_free(data);
if (done > 0 || errno == EWOULDBLOCK)
{
if (self->channel->write.state->length > 0)
if (self->super.write.state->length > 0)
socks_start_write(self);
else
socks_stop_write(self);
......@@ -234,54 +189,54 @@ static void *
oop_write_socks(oop_source *source UNUSED,
int fd, oop_event event, void *state)
{
CAST(socks_connection, self, state);
CAST(socks_channel, self, state);
assert(event == OOP_WRITE);
assert(fd == self->channel->write.fd);
assert(fd == self->super.write.fd);
if (!ssh_write_flush(self->channel->write.state, self->channel->write.fd, 0))
if (!ssh_write_flush(self->super.write.state, self->super.write.fd, 0))
{
werror("socks server: write failed: %e\n", errno);
socks_fail(self);
}
else if (!self->channel->write.state->length)
else if (!self->super.write.state->length)
socks_stop_write(self);
return OOP_CONTINUE;
}
static void
socks_start_write(struct socks_connection *self)
socks_start_write(struct socks_channel *self)
{
if (!self->channel->write.active)
if (!self->super.write.active)
{
self->channel->write.active = 1;
global_oop_source->on_fd(global_oop_source, self->channel->write.fd, OOP_WRITE,
self->super.write.active = 1;
global_oop_source->on_fd(global_oop_source, self->super.write.fd, OOP_WRITE,
oop_write_socks, self);
}
}
static void
socks_stop_write(struct socks_connection *self)
socks_stop_write(struct socks_channel *self)
{
if (self->state == SOCKS_CLOSE)
KILL_RESOURCE(&self->super);
KILL_RESOURCE(&self->super.super.super);
else if (self->channel->write.active)
else if (self->super.write.active)
{
self->channel->write.active = 0;
global_oop_source->cancel_fd(global_oop_source, self->channel->write.fd, OOP_WRITE);
self->super.write.active = 0;
global_oop_source->cancel_fd(global_oop_source, self->super.write.fd, OOP_WRITE);
}
}
static void
socks_method(struct socks_connection *self, uint8_t method)
socks_method(struct socks_channel *self, uint8_t method)
{
socks_write(self, ssh_format("%c%c", self->version, method));
}
static void
socks_reply(struct socks_connection *self,
socks_reply(struct socks_channel *self,
uint8_t status,
uint8_t atype,
uint32_t alength,
......@@ -344,95 +299,8 @@ socks2address_info(uint8_t atype,
return make_address_info(host, port);
}
/* GABA:
(class
(name socks_continuation)
(super command_continuation)
(vars
(socks object socks_connection)))
*/
static void
do_socks_continuation(struct command_continuation *s, struct lsh_object *x)
{
CAST(socks_continuation, self, s);
CAST_SUBTYPE(channel_forward, channel, x);
uint32_t left;
assert(channel == self->socks->channel);
/* We don't have the address at the server's end, so we can't pass it along. */
socks_reply(self->socks, SOCKS_ERROR_NONE, SOCKS_NOADDR, 0);
socks_stop_write(self->socks);
self->socks->channel = NULL;
self->socks->super.alive = 0;
left = SOCKS_MAX_SIZE - channel->write.state->length;
if (left > 0)
/* We used an unnecessarily small initial window. Fix it now. */
channel_adjust_rec_window(&channel->super, left);
}
static struct command_continuation *
make_socks_continuation(struct socks_connection *socks)
{
NEW(socks_continuation, self);
self->super.c = do_socks_continuation;
self->socks = socks;
return &self->super;
}
/* GABA:
(class
(name socks_exception_handler)
(super exception_handler)
(vars
(socks object socks_connection)))
*/
static void
do_exc_socks_handler(struct exception_handler *s,
const struct exception *e)
{
CAST(socks_exception_handler, self, s);
uint8_t reply = SOCKS_ERROR_GENERAL;
if (e->type == EXC_CHANNEL_OPEN)
{
if (e->subtype == SSH_OPEN_ADMINISTRATIVELY_PROHIBITED)
reply = SOCKS_ERROR_NOT_ALLOWED;
else if (e->subtype == SSH_OPEN_CONNECT_FAILED)
reply = SOCKS_ERROR_CONNECTION_REFUSED;
}
verbose("Socks forwarding denied by server: %z\n", e->msg);
socks_reply(self->socks, reply, SOCKS_NOADDR, 0);
/* FIXME: When we return, the channel will be killed by
channel_finished, and any buffered data will be discarded. We
don't try to ensure that the final reply is delivered
properly. */
socks_stop_write(self->socks);
self->socks->channel = NULL;
self->socks->super.alive = 0;
}
static struct exception_handler *
make_socks_exception_handler(struct socks_connection *socks,
const char *context)
{
NEW(socks_exception_handler, self);
self->super.raise = do_exc_socks_handler;
self->super.context = context;
self->socks = socks;
return &self->super;
}
static int
socks_command(struct socks_connection *self, uint8_t command,
socks_command(struct socks_channel *self, uint8_t command,
uint8_t addr_type, const uint8_t *addr,
uint16_t port)
{
......@@ -451,8 +319,9 @@ socks_command(struct socks_connection *self, uint8_t command,
return 0;
}
if (!channel_open_new_type(self->connection, &self->channel->super,
ATOM_DIRECT_TCPIP,
if (!channel_open_new_type(self->super.super.connection,
&self->super.super,
ATOM_LD(ATOM_DIRECT_TCPIP),
"%S%i%S%i",
target->ip, target->port,
self->peer->ip, self->peer->port))
......@@ -462,14 +331,7 @@ socks_command(struct socks_connection *self, uint8_t command,
return 0;
}
else
{
assert(!self->channel->super.channel_open_context);
self->channel->super.channel_open_context
= make_command_context(make_socks_continuation(self),
make_socks_exception_handler(self, HANDLER_CONTEXT));
return 1;
}
return 1;
}
}
......@@ -477,20 +339,20 @@ static void *
oop_read_socks(oop_source *source UNUSED,
int fd, oop_event event, void *state)
{
CAST(socks_connection, self, state);
CAST(socks_channel, self, state);
const uint8_t *p;
uint32_t to_read;
int res;
assert(event == OOP_READ);
assert(fd == self->channel->read.fd);
assert(fd == self->super.read.fd);
/* The socks client must send a single command and wait for reply.
So we can safely read all available data, and treat buffer full
as an error. After processing a command, we can also discard any
left over data, as there shouldn't be any. */
to_read = lsh_string_length(self->channel->read.buffer) - self->pos;
to_read = lsh_string_length(self->super.read.buffer) - self->pos;
if (!to_read)
{
werror("socks server: Read buffer full.\n");
......@@ -498,8 +360,8 @@ oop_read_socks(oop_source *source UNUSED,
return OOP_CONTINUE;
}
res = lsh_string_read(self->channel->read.buffer, self->pos,
self->channel->read.fd, to_read);
res = lsh_string_read(self->super.read.buffer, self->pos,
self->super.read.fd, to_read);
if (res < 0)
{
......@@ -518,10 +380,10 @@ oop_read_socks(oop_source *source UNUSED,
assert(self->pos > 0);
debug("oop_read_socks: res = %i, pos = %i, length = %i\n", res, self->pos, self->length);
while (self->super.alive && self->channel->read.active
while (self->super.super.super.alive && self->super.read.active
&& self->pos >= self->length)
{
p = lsh_string_data(self->channel->read.buffer);
p = lsh_string_data(self->super.read.buffer);
switch (self->state)
{
......@@ -673,26 +535,103 @@ oop_read_socks(oop_source *source UNUSED,
}
static void
socks_start_read(struct socks_connection *self)
socks_start_read(struct socks_channel *self)
{
if (!self->channel->read.active)
if (!self->super.read.active)
{
self->channel->read.active = 1;
global_oop_source->on_fd(global_oop_source, self->channel->read.fd, OOP_READ,
self->super.read.active = 1;
global_oop_source->on_fd(global_oop_source, self->super.read.fd, OOP_READ,
oop_read_socks, self);
}
}
static void
socks_stop_read(struct socks_connection *self)
socks_stop_read(struct socks_channel *self)
{
if (self->channel->read.active)
if (self->super.read.active)
{
self->channel->read.active = 0;
global_oop_source->cancel_fd(global_oop_source, self->channel->read.fd, OOP_READ);
self->super.read.active = 0;
global_oop_source->cancel_fd(global_oop_source, self->super.read.fd, OOP_READ);
}
}
static void
do_socks_channel_event(struct ssh_channel *s, enum channel_event event)
{
CAST_SUBTYPE(socks_channel, self, s);
switch(event)
{
case CHANNEL_EVENT_CONFIRM:
{
uint32_t left;
/* We don't have the address at the server's end, so we can't
pass it along. */
socks_reply(self, SOCKS_ERROR_NONE, SOCKS_NOADDR, 0);
socks_stop_write(self);
left = SOCKS_MAX_SIZE - self->super.write.state->length;
if (left > 0)
/* We used an unnecessarily small initial window. Fix it now. */
channel_adjust_rec_window(&self->super.super, left);
channel_forward_start_io(&self->super);
break;
}
case CHANNEL_EVENT_DENY:
verbose("Socks forwarding denied by server\n");
socks_reply(self, SOCKS_ERROR_CONNECTION_REFUSED,
SOCKS_NOADDR, 0);
/* FIXME: When we return, the channel will be killed by
channel_finished, and any buffered data will be discarded. We
don't try to ensure that the final reply is delivered
properly. */
break;
case CHANNEL_EVENT_EOF:
if (!self->super.write.state->length)
channel_forward_shutdown(&self->super);
break;
case CHANNEL_EVENT_STOP:
channel_io_stop_read(&self->super.read);
break;
case CHANNEL_EVENT_START:
channel_forward_start_read(&self->super);
break;
case CHANNEL_EVENT_CLOSE:
/* Do nothing */
break;
}
}
static struct socks_channel *
make_socks_channel(struct ssh_connection *connection,
struct listen_value *lv)
{
NEW(socks_channel, self);
init_channel_forward(&self->super, lv->fd, TCPIP_WINDOW_SIZE,
do_socks_channel_event);
self->super.super.connection = connection;
/* Worst-case margin, for any buffered reply when we take the channel
into use. */
self->super.super.rec_window_size -= SOCKS_MAX_SIZE;
self->pos = 0;
self->length = 3;
self->peer = lv->peer;
self->state = SOCKS_VERSION_HEADER;
self->length = SOCKS_HEADER_SIZE;
self->version = 0;
self->target = NULL;
return self;
}
/* The read buffer is replaced when we go into connected mode, but the
writebuffer is not */
#define SOCKS_READ_BUF_SIZE 100
......@@ -708,9 +647,9 @@ DEFINE_COMMAND2(socks_handshake)
CAST_SUBTYPE(ssh_connection, connection, a1);
CAST(listen_value, lv, a2);
struct socks_connection *self = make_socks_connection(connection, lv);
struct socks_channel *self = make_socks_channel(connection, lv);
io_register_fd(lv->fd, "socks forwarding");
remember_resource(connection->resources, &self->super);
remember_resource(connection->resources, &self->super.super.super);
socks_start_read(self);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment