|
@@ -0,0 +1,420 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * @file BSocksClient.c
|
|
|
|
|
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @section LICENSE
|
|
|
|
|
+ *
|
|
|
|
|
+ * This file is part of BadVPN.
|
|
|
|
|
+ *
|
|
|
|
|
+ * BadVPN is free software: you can redistribute it and/or modify
|
|
|
|
|
+ * it under the terms of the GNU General Public License version 2
|
|
|
|
|
+ * as published by the Free Software Foundation.
|
|
|
|
|
+ *
|
|
|
|
|
+ * BadVPN is distributed in the hope that it will be useful,
|
|
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
+ * GNU General Public License for more details.
|
|
|
|
|
+ *
|
|
|
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
|
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+#include <misc/byteorder.h>
|
|
|
|
|
+#include <system/BLog.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <socksclient/BSocksClient.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <generated/blog_channel_BSocksClient.h>
|
|
|
|
|
+
|
|
|
|
|
+#define STATE_CONNECTING 1
|
|
|
|
|
+#define STATE_SENDING_HELLO 2
|
|
|
|
|
+#define STATE_SENT_HELLO 3
|
|
|
|
|
+#define STATE_SENDING_REQUEST 4
|
|
|
|
|
+#define STATE_SENT_REQUEST 5
|
|
|
|
|
+#define STATE_RECEIVED_REPLY_HEADER 6
|
|
|
|
|
+#define STATE_UP 7
|
|
|
|
|
+
|
|
|
|
|
+#define COMPONENT_SOURCE 1
|
|
|
|
|
+#define COMPONENT_SINK 2
|
|
|
|
|
+
|
|
|
|
|
+static void report_error (BSocksClient *o, int error);
|
|
|
|
|
+static void init_control_io (BSocksClient *o);
|
|
|
|
|
+static void free_control_io (BSocksClient *o);
|
|
|
|
|
+static void init_up_io (BSocksClient *o);
|
|
|
|
|
+static void free_up_io (BSocksClient *o);
|
|
|
|
|
+static void start_receive (BSocksClient *o, uint8_t *dest, int total);
|
|
|
|
|
+static void do_receive (BSocksClient *o);
|
|
|
|
|
+static void error_handler (BSocksClient *o, int component, const void *data);
|
|
|
|
|
+static void socket_error_handler (BSocksClient *o, int event);
|
|
|
|
|
+static void connect_handler (BSocksClient *o, int event);
|
|
|
|
|
+static void recv_handler_done (BSocksClient *o, int data_len);
|
|
|
|
|
+static void send_handler_done (BSocksClient *o);
|
|
|
|
|
+
|
|
|
|
|
+void report_error (BSocksClient *o, int error)
|
|
|
|
|
+{
|
|
|
|
|
+ #ifndef NDEBUG
|
|
|
|
|
+ DEAD_ENTER(o->d_dead)
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ o->handler(o->user, error);
|
|
|
|
|
+
|
|
|
|
|
+ #ifndef NDEBUG
|
|
|
|
|
+ ASSERT(DEAD_KILLED)
|
|
|
|
|
+ DEAD_LEAVE(o->d_dead);
|
|
|
|
|
+ #endif
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void init_control_io (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ // init receiving
|
|
|
|
|
+ StreamSocketSource_Init(&o->control.recv_source, FlowErrorReporter_Create(&o->domain, COMPONENT_SOURCE), &o->sock, BReactor_PendingGroup(o->reactor));
|
|
|
|
|
+ o->control.recv_if = StreamSocketSource_GetOutput(&o->control.recv_source);
|
|
|
|
|
+ StreamRecvInterface_Receiver_Init(o->control.recv_if, (StreamRecvInterface_handler_done)recv_handler_done, o);
|
|
|
|
|
+
|
|
|
|
|
+ // init sending
|
|
|
|
|
+ StreamSocketSink_Init(&o->control.send_sink, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), &o->sock, BReactor_PendingGroup(o->reactor));
|
|
|
|
|
+ PacketStreamSender_Init(&o->control.send_sender, StreamSocketSink_GetInput(&o->control.send_sink), sizeof(o->control.msg), BReactor_PendingGroup(o->reactor));
|
|
|
|
|
+ o->control.send_if = PacketStreamSender_GetInput(&o->control.send_sender);
|
|
|
|
|
+ PacketPassInterface_Sender_Init(o->control.send_if, (PacketPassInterface_handler_done)send_handler_done, o);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void free_control_io (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ // free sending
|
|
|
|
|
+ PacketStreamSender_Free(&o->control.send_sender);
|
|
|
|
|
+ StreamSocketSink_Free(&o->control.send_sink);
|
|
|
|
|
+
|
|
|
|
|
+ // free receiving
|
|
|
|
|
+ StreamSocketSource_Free(&o->control.recv_source);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void init_up_io (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ // init receiving
|
|
|
|
|
+ StreamSocketSource_Init(&o->up.recv_source, FlowErrorReporter_Create(&o->domain, COMPONENT_SOURCE), &o->sock, BReactor_PendingGroup(o->reactor));
|
|
|
|
|
+
|
|
|
|
|
+ // init sending
|
|
|
|
|
+ StreamSocketSink_Init(&o->up.send_sink, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), &o->sock, BReactor_PendingGroup(o->reactor));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void free_up_io (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ // free sending
|
|
|
|
|
+ StreamSocketSink_Free(&o->up.send_sink);
|
|
|
|
|
+
|
|
|
|
|
+ // free receiving
|
|
|
|
|
+ StreamSocketSource_Free(&o->up.recv_source);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void start_receive (BSocksClient *o, uint8_t *dest, int total)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(total > 0)
|
|
|
|
|
+
|
|
|
|
|
+ o->control.recv_dest = dest;
|
|
|
|
|
+ o->control.recv_len = 0;
|
|
|
|
|
+ o->control.recv_total = total;
|
|
|
|
|
+
|
|
|
|
|
+ do_receive(o);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void do_receive (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(o->control.recv_len < o->control.recv_total)
|
|
|
|
|
+
|
|
|
|
|
+ StreamRecvInterface_Receiver_Recv(o->control.recv_if, o->control.recv_dest + o->control.recv_len, o->control.recv_total - o->control.recv_len);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void error_handler (BSocksClient *o, int component, const void *data)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(component == COMPONENT_SOURCE || component == COMPONENT_SINK)
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ if (o->state == STATE_UP && component == COMPONENT_SOURCE && *((int *)data) == STREAMSOCKETSOURCE_ERROR_CLOSED) {
|
|
|
|
|
+ BLog(BLOG_DEBUG, "connection closed");
|
|
|
|
|
+
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BLog(BLOG_NOTICE, "socket error (%d)", BSocket_GetError(&o->sock));
|
|
|
|
|
+
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void socket_error_handler (BSocksClient *o, int event)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(event == BSOCKET_ERROR)
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ BLog(BLOG_NOTICE, "socket error event");
|
|
|
|
|
+
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void connect_handler (BSocksClient *o, int event)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(event == BSOCKET_CONNECT)
|
|
|
|
|
+ ASSERT(o->state == STATE_CONNECTING)
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ // remove event handler
|
|
|
|
|
+ BSocket_RemoveEventHandler(&o->sock, BSOCKET_CONNECT);
|
|
|
|
|
+
|
|
|
|
|
+ // check connect result
|
|
|
|
|
+ int res = BSocket_GetConnectResult(&o->sock);
|
|
|
|
|
+ if (res != 0) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "connection failed (%d)", res);
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BLog(BLOG_DEBUG, "connected");
|
|
|
|
|
+
|
|
|
|
|
+ // init control I/O
|
|
|
|
|
+ init_control_io(o);
|
|
|
|
|
+
|
|
|
|
|
+ // send hello
|
|
|
|
|
+ o->control.msg.client_hello.header.ver = hton8(SOCKS_VERSION);
|
|
|
|
|
+ o->control.msg.client_hello.header.nmethods = hton8(1);
|
|
|
|
|
+ o->control.msg.client_hello.method.method = hton8(SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED);
|
|
|
|
|
+ PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)&o->control.msg.client_hello, sizeof(o->control.msg.client_hello));
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_SENDING_HELLO;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void recv_handler_done (BSocksClient *o, int data_len)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(data_len >= 0)
|
|
|
|
|
+ ASSERT(data_len <= o->control.recv_total - o->control.recv_len)
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ o->control.recv_len += data_len;
|
|
|
|
|
+
|
|
|
|
|
+ if (o->control.recv_len < o->control.recv_total) {
|
|
|
|
|
+ do_receive(o);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (o->state) {
|
|
|
|
|
+ case STATE_SENT_HELLO: {
|
|
|
|
|
+ BLog(BLOG_DEBUG, "received hello");
|
|
|
|
|
+
|
|
|
|
|
+ if (ntoh8(o->control.msg.server_hello.ver != SOCKS_VERSION)) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "wrong version");
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (ntoh8(o->control.msg.server_hello.method != SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED)) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "wrong method");
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // send request
|
|
|
|
|
+ o->control.msg.request.header.ver = hton8(SOCKS_VERSION);
|
|
|
|
|
+ o->control.msg.request.header.cmd = hton8(SOCKS_CMD_CONNECT);
|
|
|
|
|
+ o->control.msg.request.header.rsv = hton8(0);
|
|
|
|
|
+ int len = sizeof(o->control.msg.request.header);
|
|
|
|
|
+ switch (o->dest_addr.type) {
|
|
|
|
|
+ case BADDR_TYPE_IPV4:
|
|
|
|
|
+ o->control.msg.request.header.atyp = hton8(SOCKS_ATYP_IPV4);
|
|
|
|
|
+ o->control.msg.request.addr.ipv4.addr = o->dest_addr.ipv4.ip;
|
|
|
|
|
+ o->control.msg.request.addr.ipv4.port = o->dest_addr.ipv4.port;
|
|
|
|
|
+ len += sizeof(o->control.msg.request.addr.ipv4);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case BADDR_TYPE_IPV6:
|
|
|
|
|
+ o->control.msg.request.header.atyp = hton8(SOCKS_ATYP_IPV6);
|
|
|
|
|
+ memcpy(o->control.msg.request.addr.ipv6.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip));
|
|
|
|
|
+ o->control.msg.request.addr.ipv6.port = o->dest_addr.ipv6.port;
|
|
|
|
|
+ len += sizeof(o->control.msg.request.addr.ipv6);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ ASSERT(0);
|
|
|
|
|
+ }
|
|
|
|
|
+ PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)&o->control.msg.request, len);
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_SENDING_REQUEST;
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ case STATE_SENT_REQUEST: {
|
|
|
|
|
+ BLog(BLOG_DEBUG, "received reply header");
|
|
|
|
|
+
|
|
|
|
|
+ if (ntoh8(o->control.msg.reply.header.ver) != SOCKS_VERSION) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "wrong version");
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (ntoh8(o->control.msg.reply.header.rep) != SOCKS_REP_SUCCEEDED) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "reply not successful");
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int addr_len;
|
|
|
|
|
+ switch (ntoh8(o->control.msg.reply.header.atyp)) {
|
|
|
|
|
+ case SOCKS_ATYP_IPV4:
|
|
|
|
|
+ addr_len = sizeof(o->control.msg.reply.addr.ipv4);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case SOCKS_ATYP_IPV6:
|
|
|
|
|
+ addr_len = sizeof(o->control.msg.reply.addr.ipv6);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ BLog(BLOG_NOTICE, "reply has unknown address type");
|
|
|
|
|
+ report_error(o, BSOCKSCLIENT_EVENT_ERROR);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // receive the rest of the reply
|
|
|
|
|
+ start_receive(o, (uint8_t *)&o->control.msg.reply.addr, addr_len);
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_RECEIVED_REPLY_HEADER;
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ case STATE_RECEIVED_REPLY_HEADER: {
|
|
|
|
|
+ BLog(BLOG_DEBUG, "received reply rest");
|
|
|
|
|
+
|
|
|
|
|
+ // free control I/O
|
|
|
|
|
+ free_control_io(o);
|
|
|
|
|
+
|
|
|
|
|
+ // init up I/O
|
|
|
|
|
+ init_up_io(o);
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_UP;
|
|
|
|
|
+
|
|
|
|
|
+ // call handler
|
|
|
|
|
+ o->handler(o->user, BSOCKSCLIENT_EVENT_UP);
|
|
|
|
|
+ return;
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ ASSERT(0);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void send_handler_done (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ switch (o->state) {
|
|
|
|
|
+ case STATE_SENDING_HELLO: {
|
|
|
|
|
+ BLog(BLOG_DEBUG, "sent hello");
|
|
|
|
|
+
|
|
|
|
|
+ // receive hello
|
|
|
|
|
+ start_receive(o, (uint8_t *)&o->control.msg.server_hello, sizeof(o->control.msg.server_hello));
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_SENT_HELLO;
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ case STATE_SENDING_REQUEST: {
|
|
|
|
|
+ BLog(BLOG_DEBUG, "sent request");
|
|
|
|
|
+
|
|
|
|
|
+ // receive reply header
|
|
|
|
|
+ start_receive(o, (uint8_t *)&o->control.msg.reply.header, sizeof(o->control.msg.reply.header));
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_SENT_REQUEST;
|
|
|
|
|
+ } break;
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ ASSERT(0);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int BSocksClient_Init (BSocksClient *o, BAddr server_addr, BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(dest_addr.type == BADDR_TYPE_IPV4 || dest_addr.type == BADDR_TYPE_IPV6)
|
|
|
|
|
+
|
|
|
|
|
+ // init arguments
|
|
|
|
|
+ o->dest_addr = dest_addr;
|
|
|
|
|
+ o->handler = handler;
|
|
|
|
|
+ o->user = user;
|
|
|
|
|
+ o->reactor = reactor;
|
|
|
|
|
+
|
|
|
|
|
+ // init error domain
|
|
|
|
|
+ FlowErrorDomain_Init(&o->domain, (FlowErrorDomain_handler)error_handler, o);
|
|
|
|
|
+
|
|
|
|
|
+ // init socket
|
|
|
|
|
+ if (BSocket_Init(&o->sock, o->reactor, server_addr.type, BSOCKET_TYPE_STREAM) < 0) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "BSocket_Init failed");
|
|
|
|
|
+ goto fail0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // connect socket
|
|
|
|
|
+ if (BSocket_Connect(&o->sock, &server_addr) >= 0 || BSocket_GetError(&o->sock) != BSOCKET_ERROR_IN_PROGRESS) {
|
|
|
|
|
+ BLog(BLOG_NOTICE, "BSocket_Connect failed");
|
|
|
|
|
+ goto fail1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // setup error event
|
|
|
|
|
+ BSocket_AddEventHandler(&o->sock, BSOCKET_ERROR, (BSocket_handler)socket_error_handler, o);
|
|
|
|
|
+ BSocket_EnableEvent(&o->sock, BSOCKET_ERROR);
|
|
|
|
|
+
|
|
|
|
|
+ // setup connect event
|
|
|
|
|
+ BSocket_AddEventHandler(&o->sock, BSOCKET_CONNECT, (BSocket_handler)connect_handler, o);
|
|
|
|
|
+ BSocket_EnableEvent(&o->sock, BSOCKET_CONNECT);
|
|
|
|
|
+
|
|
|
|
|
+ // set state
|
|
|
|
|
+ o->state = STATE_CONNECTING;
|
|
|
|
|
+
|
|
|
|
|
+ DebugObject_Init(&o->d_obj);
|
|
|
|
|
+ #ifndef NDEBUG
|
|
|
|
|
+ DEAD_INIT(o->d_dead);
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ return 1;
|
|
|
|
|
+
|
|
|
|
|
+fail1:
|
|
|
|
|
+ BSocket_Free(&o->sock);
|
|
|
|
|
+fail0:
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void BSocksClient_Free (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ #ifndef NDEBUG
|
|
|
|
|
+ DEAD_KILL(o->d_dead);
|
|
|
|
|
+ #endif
|
|
|
|
|
+ DebugObject_Free(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ if (o->state == STATE_UP) {
|
|
|
|
|
+ // free up I/O
|
|
|
|
|
+ free_up_io(o);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (o->state != STATE_CONNECTING) {
|
|
|
|
|
+ ASSERT(o->state == STATE_SENDING_HELLO || o->state == STATE_SENT_HELLO ||
|
|
|
|
|
+ o->state == STATE_SENDING_REQUEST || o->state == STATE_SENT_REQUEST ||
|
|
|
|
|
+ o->state == STATE_RECEIVED_REPLY_HEADER
|
|
|
|
|
+ )
|
|
|
|
|
+ // free control I/O
|
|
|
|
|
+ free_control_io(o);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // free socket
|
|
|
|
|
+ BSocket_Free(&o->sock);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(o->state == STATE_UP)
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ return StreamSocketSink_GetInput(&o->up.send_sink);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o)
|
|
|
|
|
+{
|
|
|
|
|
+ ASSERT(o->state == STATE_UP)
|
|
|
|
|
+ DebugObject_Access(&o->d_obj);
|
|
|
|
|
+
|
|
|
|
|
+ return StreamSocketSource_GetOutput(&o->up.recv_source);
|
|
|
|
|
+}
|