BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file BSocksClient.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <string.h>
31  
32 #include <misc/byteorder.h>
33 #include <misc/balloc.h>
34 #include <base/BLog.h>
35  
36 #include <socksclient/BSocksClient.h>
37  
38 #include <generated/blog_channel_BSocksClient.h>
39  
40 #define STATE_CONNECTING 1
41 #define STATE_CONNECTED_HANDLER 2
42 #define STATE_SENDING_HELLO 3
43 #define STATE_SENT_HELLO 4
44 #define STATE_SENDING_PASSWORD 5
45 #define STATE_SENT_PASSWORD 6
46 #define STATE_SENDING_REQUEST 7
47 #define STATE_SENT_REQUEST 8
48 #define STATE_RECEIVED_REPLY_HEADER 9
49 #define STATE_UP 10
50  
51 static void report_error (BSocksClient *o, int error);
52 static void init_control_io (BSocksClient *o);
53 static void free_control_io (BSocksClient *o);
54 static void init_up_io (BSocksClient *o);
55 static void free_up_io (BSocksClient *o);
56 static int reserve_buffer (BSocksClient *o, bsize_t size);
57 static void start_receive (BSocksClient *o, uint8_t *dest, int total);
58 static void do_receive (BSocksClient *o);
59 static void connector_handler (BSocksClient* o, int is_error);
60 static void connection_handler (BSocksClient* o, int event);
61 static void continue_job_handler (BSocksClient *o);
62 static void recv_handler_done (BSocksClient *o, int data_len);
63 static void send_handler_done (BSocksClient *o);
64 static void auth_finished (BSocksClient *p);
65  
66 void report_error (BSocksClient *o, int error)
67 {
68 DEBUGERROR(&o->d_err, o->handler(o->user, error))
69 }
70  
71 void init_control_io (BSocksClient *o)
72 {
73 // init receiving
74 BConnection_RecvAsync_Init(&o->con);
75 o->control.recv_if = BConnection_RecvAsync_GetIf(&o->con);
76 StreamRecvInterface_Receiver_Init(o->control.recv_if, (StreamRecvInterface_handler_done)recv_handler_done, o);
77  
78 // init sending
79 BConnection_SendAsync_Init(&o->con);
80 PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), INT_MAX, BReactor_PendingGroup(o->reactor));
81 o->control.send_if = PacketStreamSender_GetInput(&o->control.send_sender);
82 PacketPassInterface_Sender_Init(o->control.send_if, (PacketPassInterface_handler_done)send_handler_done, o);
83 }
84  
85 void free_control_io (BSocksClient *o)
86 {
87 // free sending
88 PacketStreamSender_Free(&o->control.send_sender);
89 BConnection_SendAsync_Free(&o->con);
90  
91 // free receiving
92 BConnection_RecvAsync_Free(&o->con);
93 }
94  
95 void init_up_io (BSocksClient *o)
96 {
97 // init receiving
98 BConnection_RecvAsync_Init(&o->con);
99  
100 // init sending
101 BConnection_SendAsync_Init(&o->con);
102 }
103  
104 void free_up_io (BSocksClient *o)
105 {
106 // free sending
107 BConnection_SendAsync_Free(&o->con);
108  
109 // free receiving
110 BConnection_RecvAsync_Free(&o->con);
111 }
112  
113 int reserve_buffer (BSocksClient *o, bsize_t size)
114 {
115 if (size.is_overflow) {
116 BLog(BLOG_ERROR, "size overflow");
117 return 0;
118 }
119  
120 char *buffer = (char *)BRealloc(o->buffer, size.value);
121 if (!buffer) {
122 BLog(BLOG_ERROR, "BRealloc failed");
123 return 0;
124 }
125  
126 o->buffer = buffer;
127  
128 return 1;
129 }
130  
131 void start_receive (BSocksClient *o, uint8_t *dest, int total)
132 {
133 ASSERT(total > 0)
134  
135 o->control.recv_dest = dest;
136 o->control.recv_len = 0;
137 o->control.recv_total = total;
138  
139 do_receive(o);
140 }
141  
142 void do_receive (BSocksClient *o)
143 {
144 ASSERT(o->control.recv_len < o->control.recv_total)
145  
146 StreamRecvInterface_Receiver_Recv(o->control.recv_if, o->control.recv_dest + o->control.recv_len, o->control.recv_total - o->control.recv_len);
147 }
148  
149 void connector_handler (BSocksClient* o, int is_error)
150 {
151 DebugObject_Access(&o->d_obj);
152 ASSERT(o->state == STATE_CONNECTING)
153  
154 // check connection result
155 if (is_error) {
156 BLog(BLOG_ERROR, "connection failed");
157 goto fail0;
158 }
159  
160 // init connection
161 if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) {
162 BLog(BLOG_ERROR, "BConnection_Init failed");
163 goto fail0;
164 }
165  
166 BLog(BLOG_DEBUG, "connected");
167  
168 // init control I/O
169 init_control_io(o);
170  
171 // go to STATE_CONNECTED_HANDLER and set the continue job in order to continue
172 // in continue_job_handler
173 o->state = STATE_CONNECTED_HANDLER;
174 BPending_Set(&o->continue_job);
175  
176 // call the handler with the connected event
177 o->handler(o->user, BSOCKSCLIENT_EVENT_CONNECTED);
178 return;
179  
180 fail0:
181 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
182 return;
183 }
184  
185 void connection_handler (BSocksClient* o, int event)
186 {
187 DebugObject_Access(&o->d_obj);
188 ASSERT(o->state != STATE_CONNECTING)
189  
190 if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) {
191 report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED);
192 return;
193 }
194  
195 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
196 return;
197 }
198  
199 void continue_job_handler (BSocksClient *o)
200 {
201 DebugObject_Access(&o->d_obj);
202 ASSERT(o->state == STATE_CONNECTED_HANDLER)
203  
204 // check number of methods
205 if (o->num_auth_info == 0 || o->num_auth_info > 255) {
206 BLog(BLOG_ERROR, "invalid number of authentication methods");
207 goto fail0;
208 }
209  
210 // allocate buffer for sending hello
211 bsize_t size = bsize_add(
212 bsize_fromsize(sizeof(struct socks_client_hello_header)),
213 bsize_mul(
214 bsize_fromsize(o->num_auth_info),
215 bsize_fromsize(sizeof(struct socks_client_hello_method))
216 )
217 );
218 if (!reserve_buffer(o, size)) {
219 goto fail0;
220 }
221  
222 // write hello header
223 struct socks_client_hello_header header;
224 header.ver = hton8(SOCKS_VERSION);
225 header.nmethods = hton8(o->num_auth_info);
226 memcpy(o->buffer, &header, sizeof(header));
227  
228 // write hello methods
229 for (size_t i = 0; i < o->num_auth_info; i++) {
230 struct socks_client_hello_method method;
231 method.method = hton8(o->auth_info[i].auth_type);
232 memcpy(o->buffer + sizeof(header) + i * sizeof(method), &method, sizeof(method));
233 }
234  
235 // send
236 PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value);
237  
238 // set state
239 o->state = STATE_SENDING_HELLO;
240  
241 return;
242  
243 fail0:
244 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
245 return;
246 }
247  
248 void recv_handler_done (BSocksClient *o, int data_len)
249 {
250 ASSERT(data_len >= 0)
251 ASSERT(data_len <= o->control.recv_total - o->control.recv_len)
252 DebugObject_Access(&o->d_obj);
253  
254 o->control.recv_len += data_len;
255  
256 if (o->control.recv_len < o->control.recv_total) {
257 do_receive(o);
258 return;
259 }
260  
261 switch (o->state) {
262 case STATE_SENT_HELLO: {
263 BLog(BLOG_DEBUG, "received hello");
264  
265 struct socks_server_hello imsg;
266 memcpy(&imsg, o->buffer, sizeof(imsg));
267  
268 if (ntoh8(imsg.ver) != SOCKS_VERSION) {
269 BLog(BLOG_NOTICE, "wrong version");
270 goto fail;
271 }
272  
273 size_t auth_index;
274 for (auth_index = 0; auth_index < o->num_auth_info; auth_index++) {
275 if (o->auth_info[auth_index].auth_type == ntoh8(imsg.method)) {
276 break;
277 }
278 }
279  
280 if (auth_index == o->num_auth_info) {
281 BLog(BLOG_NOTICE, "server didn't accept any authentication method");
282 goto fail;
283 }
284  
285 const struct BSocksClient_auth_info *ai = &o->auth_info[auth_index];
286  
287 switch (ai->auth_type) {
288 case SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED: {
289 BLog(BLOG_DEBUG, "no authentication");
290  
291 auth_finished(o);
292 } break;
293  
294 case SOCKS_METHOD_USERNAME_PASSWORD: {
295 BLog(BLOG_DEBUG, "password authentication");
296  
297 if (ai->password.username_len == 0 || ai->password.username_len > 255 ||
298 ai->password.password_len == 0 || ai->password.password_len > 255
299 ) {
300 BLog(BLOG_NOTICE, "invalid username/password length");
301 goto fail;
302 }
303  
304 // allocate password packet
305 bsize_t size = bsize_fromsize(1 + 1 + ai->password.username_len + 1 + ai->password.password_len);
306 if (!reserve_buffer(o, size)) {
307 goto fail;
308 }
309  
310 // write password packet
311 char *ptr = o->buffer;
312 *ptr++ = 1;
313 *ptr++ = ai->password.username_len;
314 memcpy(ptr, ai->password.username, ai->password.username_len);
315 ptr += ai->password.username_len;
316 *ptr++ = ai->password.password_len;
317 memcpy(ptr, ai->password.password, ai->password.password_len);
318 ptr += ai->password.password_len;
319  
320 // start sending
321 PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value);
322  
323 // set state
324 o->state = STATE_SENDING_PASSWORD;
325 } break;
326  
327 default: ASSERT(0);
328 }
329 } break;
330  
331 case STATE_SENT_REQUEST: {
332 BLog(BLOG_DEBUG, "received reply header");
333  
334 struct socks_reply_header imsg;
335 memcpy(&imsg, o->buffer, sizeof(imsg));
336  
337 if (ntoh8(imsg.ver) != SOCKS_VERSION) {
338 BLog(BLOG_NOTICE, "wrong version");
339 goto fail;
340 }
341  
342 if (ntoh8(imsg.rep) != SOCKS_REP_SUCCEEDED) {
343 BLog(BLOG_NOTICE, "reply not successful");
344 goto fail;
345 }
346  
347 int addr_len;
348 switch (ntoh8(imsg.atyp)) {
349 case SOCKS_ATYP_IPV4:
350 o->bind_addr.type = BADDR_TYPE_IPV4;
351 addr_len = sizeof(struct socks_addr_ipv4);
352 break;
353 case SOCKS_ATYP_IPV6:
354 o->bind_addr.type = BADDR_TYPE_IPV6;
355 addr_len = sizeof(struct socks_addr_ipv6);
356 break;
357 default:
358 BLog(BLOG_NOTICE, "reply has unknown address type");
359 goto fail;
360 }
361  
362 // receive the rest of the reply
363 start_receive(o, (uint8_t *)o->buffer + sizeof(imsg), addr_len);
364  
365 // set state
366 o->state = STATE_RECEIVED_REPLY_HEADER;
367 } break;
368  
369 case STATE_SENT_PASSWORD: {
370 BLog(BLOG_DEBUG, "received password reply");
371  
372 if (o->buffer[0] != 1) {
373 BLog(BLOG_NOTICE, "password reply has unknown version");
374 goto fail;
375 }
376  
377 if (o->buffer[1] != 0) {
378 BLog(BLOG_NOTICE, "password reply is negative");
379 goto fail;
380 }
381  
382 auth_finished(o);
383 } break;
384  
385 case STATE_RECEIVED_REPLY_HEADER: {
386 BLog(BLOG_DEBUG, "received reply rest");
387  
388 // Record the address of the new socket bound by the server.
389 // For a CONNECT command, this is the address of the TCP client socket to dest_addr.
390 // Knowing this address is usually not important.
391 // For a UDP_ASSOCIATE command, this is the UDP address to which to send SOCKS UDP.
392 // Recording this address is a prerequisite to send traffic on a SOCKS-UDP association.
393 void *addr_buffer = o->buffer + sizeof(struct socks_reply_header);
394 switch (o->bind_addr.type) {
395 case BADDR_TYPE_IPV4: {
396 struct socks_addr_ipv4 ip4;
397 memcpy(&ip4, addr_buffer, sizeof(ip4));
398 o->bind_addr.ipv4.ip = ip4.addr;
399 o->bind_addr.ipv4.port = ip4.port;
400 } break;
401 case BADDR_TYPE_IPV6: {
402 struct socks_addr_ipv6 ip6;
403 memcpy(&ip6, addr_buffer, sizeof(ip6));
404 memcpy(o->bind_addr.ipv6.ip, ip6.addr, sizeof(ip6.addr));
405 o->bind_addr.ipv6.port = ip6.port;
406 } break;
407 default: ASSERT(0);
408 }
409  
410 // free buffer
411 BFree(o->buffer);
412 o->buffer = NULL;
413  
414 // free control I/O
415 free_control_io(o);
416  
417 // init up I/O
418 // Initializing this is not needed for UDP ASSOCIATE but it doesn't hurt.
419 // We anyway don't allow the user to use these interfaces in that case.
420 init_up_io(o);
421  
422 // set state
423 o->state = STATE_UP;
424  
425 // call handler
426 o->handler(o->user, BSOCKSCLIENT_EVENT_UP);
427 return;
428 } break;
429  
430 default:
431 ASSERT(0);
432 }
433  
434 return;
435  
436 fail:
437 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
438 }
439  
440 void send_handler_done (BSocksClient *o)
441 {
442 DebugObject_Access(&o->d_obj);
443 ASSERT(o->buffer)
444  
445 switch (o->state) {
446 case STATE_SENDING_HELLO: {
447 BLog(BLOG_DEBUG, "sent hello");
448  
449 // allocate buffer for receiving hello
450 bsize_t size = bsize_fromsize(sizeof(struct socks_server_hello));
451 if (!reserve_buffer(o, size)) {
452 goto fail;
453 }
454  
455 // receive hello
456 start_receive(o, (uint8_t *)o->buffer, size.value);
457  
458 // set state
459 o->state = STATE_SENT_HELLO;
460 } break;
461  
462 case STATE_SENDING_REQUEST: {
463 BLog(BLOG_DEBUG, "sent request");
464  
465 // allocate buffer for receiving reply
466 bsize_t size = bsize_add(
467 bsize_fromsize(sizeof(struct socks_reply_header)),
468 bsize_max(bsize_fromsize(sizeof(struct socks_addr_ipv4)), bsize_fromsize(sizeof(struct socks_addr_ipv6)))
469 );
470 if (!reserve_buffer(o, size)) {
471 goto fail;
472 }
473  
474 // receive reply header
475 start_receive(o, (uint8_t *)o->buffer, sizeof(struct socks_reply_header));
476  
477 // set state
478 o->state = STATE_SENT_REQUEST;
479 } break;
480  
481 case STATE_SENDING_PASSWORD: {
482 BLog(BLOG_DEBUG, "send password");
483  
484 // allocate buffer for receiving reply
485 bsize_t size = bsize_fromsize(2);
486 if (!reserve_buffer(o, size)) {
487 goto fail;
488 }
489  
490 // receive reply header
491 start_receive(o, (uint8_t *)o->buffer, size.value);
492  
493 // set state
494 o->state = STATE_SENT_PASSWORD;
495 } break;
496  
497 default:
498 ASSERT(0);
499 }
500  
501 return;
502  
503 fail:
504 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
505 }
506  
507 void auth_finished (BSocksClient *o)
508 {
509 // allocate request buffer
510 bsize_t size = bsize_fromsize(sizeof(struct socks_request_header));
511 switch (o->dest_addr.type) {
512 case BADDR_TYPE_IPV4:
513 size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4)));
514 break;
515 case BADDR_TYPE_IPV6:
516 size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6)));
517 break;
518 default:
519 BLog(BLOG_ERROR, "Invalid dest_addr address type.");
520 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
521 return;
522 }
523 if (!reserve_buffer(o, size)) {
524 report_error(o, BSOCKSCLIENT_EVENT_ERROR);
525 return;
526 }
527  
528 // write request
529 struct socks_request_header header;
530 header.ver = hton8(SOCKS_VERSION);
531 header.cmd = hton8(o->udp ? SOCKS_CMD_UDP_ASSOCIATE : SOCKS_CMD_CONNECT);
532 header.rsv = hton8(0);
533 switch (o->dest_addr.type) {
534 case BADDR_TYPE_IPV4: {
535 header.atyp = hton8(SOCKS_ATYP_IPV4);
536 struct socks_addr_ipv4 addr;
537 addr.addr = o->dest_addr.ipv4.ip;
538 addr.port = o->dest_addr.ipv4.port;
539 memcpy(o->buffer + sizeof(header), &addr, sizeof(addr));
540 } break;
541 case BADDR_TYPE_IPV6: {
542 header.atyp = hton8(SOCKS_ATYP_IPV6);
543 struct socks_addr_ipv6 addr;
544 memcpy(addr.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip));
545 addr.port = o->dest_addr.ipv6.port;
546 memcpy(o->buffer + sizeof(header), &addr, sizeof(addr));
547 } break;
548 default:
549 ASSERT(0);
550 }
551 memcpy(o->buffer, &header, sizeof(header));
552  
553 // send request
554 PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value);
555  
556 // set state
557 o->state = STATE_SENDING_REQUEST;
558 }
559  
560 struct BSocksClient_auth_info BSocksClient_auth_none (void)
561 {
562 struct BSocksClient_auth_info info;
563 info.auth_type = SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED;
564 return info;
565 }
566  
567 struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len)
568 {
569 struct BSocksClient_auth_info info;
570 info.auth_type = SOCKS_METHOD_USERNAME_PASSWORD;
571 info.password.username = username;
572 info.password.username_len = username_len;
573 info.password.password = password;
574 info.password.password_len = password_len;
575 return info;
576 }
577  
578 int BSocksClient_Init (BSocksClient *o, BAddr server_addr,
579 const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, BAddr dest_addr,
580 bool udp, BSocksClient_handler handler, void *user, BReactor *reactor)
581 {
582 ASSERT(!BAddr_IsInvalid(&server_addr))
583 #ifndef NDEBUG
584 for (size_t i = 0; i < num_auth_info; i++) {
585 ASSERT(auth_info[i].auth_type == SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED ||
586 auth_info[i].auth_type == SOCKS_METHOD_USERNAME_PASSWORD)
587 }
588 #endif
589  
590 // init arguments
591 o->auth_info = auth_info;
592 o->num_auth_info = num_auth_info;
593 o->dest_addr = dest_addr;
594 o->udp = udp;
595 o->handler = handler;
596 o->user = user;
597 o->reactor = reactor;
598  
599 // set no buffer
600 o->buffer = NULL;
601  
602 // init continue_job
603 BPending_Init(&o->continue_job, BReactor_PendingGroup(o->reactor),
604 (BPending_handler)continue_job_handler, o);
605  
606 // init connector
607 if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) {
608 BLog(BLOG_ERROR, "BConnector_Init failed");
609 goto fail0;
610 }
611  
612 // set state
613 o->state = STATE_CONNECTING;
614  
615 DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
616 DebugObject_Init(&o->d_obj);
617 return 1;
618  
619 fail0:
620 BPending_Free(&o->continue_job);
621 return 0;
622 }
623  
624 void BSocksClient_Free (BSocksClient *o)
625 {
626 DebugObject_Free(&o->d_obj);
627 DebugError_Free(&o->d_err);
628  
629 if (o->state != STATE_CONNECTING) {
630 if (o->state == STATE_UP) {
631 // free up I/O
632 free_up_io(o);
633 } else {
634 // free control I/O
635 free_control_io(o);
636 }
637  
638 // free connection
639 BConnection_Free(&o->con);
640 }
641  
642 // free connector
643 BConnector_Free(&o->connector);
644  
645 // free continue job
646 BPending_Free(&o->continue_job);
647  
648 // free buffer
649 if (o->buffer) {
650 BFree(o->buffer);
651 }
652 }
653  
654 int BSocksClient_GetLocalAddr (BSocksClient *o, BAddr *local_addr)
655 {
656 ASSERT(o->state != STATE_CONNECTING)
657 DebugObject_Access(&o->d_obj);
658  
659 return BConnection_GetLocalAddress(&o->con, local_addr);
660 }
661  
662 void BSocksClient_SetDestAddr (BSocksClient *o, BAddr dest_addr)
663 {
664 ASSERT(o->state == STATE_CONNECTING || o->state == STATE_CONNECTED_HANDLER)
665 DebugObject_Access(&o->d_obj);
666  
667 o->dest_addr = dest_addr;
668 }
669  
670 BAddr BSocksClient_GetBindAddr (BSocksClient *o)
671 {
672 ASSERT(o->state == STATE_UP)
673 DebugObject_Access(&o->d_obj);
674  
675 return o->bind_addr;
676 }
677  
678 StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o)
679 {
680 ASSERT(o->state == STATE_UP)
681 ASSERT(!o->udp)
682 DebugObject_Access(&o->d_obj);
683  
684 return BConnection_SendAsync_GetIf(&o->con);
685 }
686  
687 StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o)
688 {
689 ASSERT(o->state == STATE_UP)
690 ASSERT(!o->udp)
691 DebugObject_Access(&o->d_obj);
692  
693 return BConnection_RecvAsync_GetIf(&o->con);
694 }