Просмотр исходного кода

Replace BSocket with BConnection and BDatagram. On Windows, do all I/O through IOCP.

ambrop7 14 лет назад
Родитель
Сommit
230ac03a87
81 измененных файлов с 5895 добавлено и 6543 удалено
  1. 4 6
      blog_channels.txt
  2. 0 1
      client/CMakeLists.txt
  3. 76 91
      client/DatagramPeerIO.c
  4. 26 24
      client/DatagramPeerIO.h
  5. 123 137
      client/PasswordListener.c
  6. 13 11
      client/PasswordListener.h
  7. 0 106
      client/PasswordSender.c
  8. 0 99
      client/PasswordSender.h
  9. 255 280
      client/StreamPeerIO.c
  10. 10 21
      client/StreamPeerIO.h
  11. 27 25
      client/client.c
  12. 34 29
      dhcpclient/BDHCPClient.c
  13. 4 7
      dhcpclient/BDHCPClient.h
  14. 13 0
      examples/dhcpclient_test.c
  15. 21 20
      examples/stdin_input.c
  16. 8 12
      flooder/flooder.c
  17. 2 0
      flow/CMakeLists.txt
  18. 0 4
      flowextra/CMakeLists.txt
  19. 0 141
      flowextra/DatagramSocketSink.c
  20. 0 100
      flowextra/DatagramSocketSink.h
  21. 0 161
      flowextra/DatagramSocketSource.c
  22. 0 102
      flowextra/DatagramSocketSource.h
  23. 0 122
      flowextra/StreamSocketSink.c
  24. 0 82
      flowextra/StreamSocketSink.h
  25. 0 128
      flowextra/StreamSocketSource.c
  26. 0 84
      flowextra/StreamSocketSource.h
  27. 1 1
      generated/blog_channel_BConnection.h
  28. 1 1
      generated/blog_channel_BDatagram.h
  29. 1 1
      generated/blog_channel_BNetwork.h
  30. 1 1
      generated/blog_channel_BSSLConnection.h
  31. 0 4
      generated/blog_channel_DatagramSocketSource.h
  32. 0 4
      generated/blog_channel_StreamSocketSource.h
  33. 31 33
      generated/blog_channels_defines.h
  34. 4 6
      generated/blog_channels_list.h
  35. 21 44
      inputprocess/BInputProcess.c
  36. 2 5
      inputprocess/BInputProcess.h
  37. 3 4
      misc/sslsocket.h
  38. 16 2
      ncd/modules/net_ipv4_dhcp.c
  39. 4 4
      ncd/ncd.c
  40. 0 327
      nspr_support/BPRFileDesc.c
  41. 0 143
      nspr_support/BPRFileDesc.h
  42. 658 0
      nspr_support/BSSLConnection.c
  43. 86 0
      nspr_support/BSSLConnection.h
  44. 0 297
      nspr_support/BSocketPRFileDesc.c
  45. 0 54
      nspr_support/BSocketPRFileDesc.h
  46. 2 5
      nspr_support/CMakeLists.txt
  47. 0 124
      nspr_support/PRStreamSink.c
  48. 0 82
      nspr_support/PRStreamSink.h
  49. 0 130
      nspr_support/PRStreamSource.c
  50. 0 84
      nspr_support/PRStreamSource.h
  51. 76 122
      server/server.c
  52. 6 20
      server/server.h
  53. 85 90
      server_connection/ServerConnection.c
  54. 8 21
      server_connection/ServerConnection.h
  55. 64 85
      socksclient/BSocksClient.c
  56. 3 11
      socksclient/BSocksClient.h
  57. 0 4
      system/BAddr.h
  58. 136 0
      system/BConnection.h
  59. 793 0
      system/BConnection_unix.c
  60. 81 0
      system/BConnection_unix.h
  61. 858 0
      system/BConnection_win.c
  62. 94 0
      system/BConnection_win.h
  63. 66 0
      system/BDatagram.h
  64. 769 0
      system/BDatagram_unix.c
  65. 64 0
      system/BDatagram_unix.h
  66. 738 0
      system/BDatagram_win.c
  67. 92 0
      system/BDatagram_win.h
  68. 88 0
      system/BNetwork.c
  69. 29 0
      system/BNetwork.h
  70. 166 116
      system/BReactor.c
  71. 34 85
      system/BReactor.h
  72. 32 45
      system/BSignal.c
  73. 0 1746
      system/BSocket.c
  74. 0 492
      system/BSocket.h
  75. 10 4
      system/CMakeLists.txt
  76. 0 157
      system/Listener.c
  77. 0 104
      system/Listener.h
  78. 4 4
      tun2socks/tun2socks.c
  79. 76 194
      tuntap/BTap.c
  80. 5 9
      tuntap/BTap.h
  81. 71 85
      udpgw/udpgw.c

+ 4 - 6
blog_channels.txt

@@ -51,13 +51,8 @@ BDHCPClient 4
 NCDIfConfig 4
 BUnixSignal 4
 BProcess 4
-StreamSocketSink 4
-StreamSocketSource 4
-DatagramSocketSink 4
-DatagramSocketSource 4
 PRStreamSink 4
 PRStreamSource 4
-BSocketPRFileDesc 4
 PacketProtoDecoder 4
 DPRelay 4
 BThreadWork 4
@@ -68,7 +63,6 @@ NCDUdevMonitor 4
 NCDUdevCache 4
 NCDUdevManager 4
 BTime 4
-BSocket 4
 BEncryption 4
 SPProtoDecoder 4
 LineBuffer 4
@@ -83,3 +77,7 @@ NCDRfkillMonitor 4
 udpgw 4
 UdpGwClient 4
 SocksUdpGwClient 4
+BNetwork 4
+BConnection 4
+BSSLConnection 4
+BDatagram 4

+ 0 - 1
client/CMakeLists.txt

@@ -4,7 +4,6 @@ add_executable(badvpn-client
     DatagramPeerIO.c
     PasswordListener.c
     DataProto.c
-    PasswordSender.c
     FrameDecider.c
     DPRelay.c
     DPReceive.c

+ 76 - 91
client/DatagramPeerIO.c

@@ -36,56 +36,57 @@
 #define DATAGRAMPEERIO_COMPONENT_SINK 1
 #define DATAGRAMPEERIO_COMPONENT_SOURCE 2
 
-static void init_sending (DatagramPeerIO *o, BAddr addr, BIPAddr local_addr);
-static void free_sending (DatagramPeerIO *o);
-static void init_receiving (DatagramPeerIO *o);
-static void free_receiving (DatagramPeerIO *o);
-static void error_handler (DatagramPeerIO *o, int component, int code);
+static void init_io (DatagramPeerIO *o);
+static void free_io (DatagramPeerIO *o);
+static void dgram_handler (DatagramPeerIO *o, int event);
 static void reset_mode (DatagramPeerIO *o);
 static void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len);
 
-void init_sending (DatagramPeerIO *o, BAddr addr, BIPAddr local_addr)
+void init_io (DatagramPeerIO *o)
 {
-    // init sink
-    DatagramSocketSink_Init(&o->send_sink, FlowErrorReporter_Create(&o->domain, DATAGRAMPEERIO_COMPONENT_SINK), &o->sock, o->effective_socket_mtu, addr, local_addr, BReactor_PendingGroup(o->reactor));
+    // init dgram recv interface
+    BDatagram_RecvAsync_Init(&o->dgram, o->effective_socket_mtu);
+    
+    // connect source
+    PacketRecvConnector_ConnectInput(&o->recv_connector, BDatagram_RecvAsync_GetIf(&o->dgram));
+    
+    // init dgram send interface
+    BDatagram_SendAsync_Init(&o->dgram, o->effective_socket_mtu);
     
     // connect sink
-    PacketPassConnector_ConnectOutput(&o->send_connector, DatagramSocketSink_GetInput(&o->send_sink));
+    PacketPassConnector_ConnectOutput(&o->send_connector, BDatagram_SendAsync_GetIf(&o->dgram));
 }
 
-void free_sending (DatagramPeerIO *o)
+void free_io (DatagramPeerIO *o)
 {
     // disconnect sink
     PacketPassConnector_DisconnectOutput(&o->send_connector);
     
-    // free sink
-    DatagramSocketSink_Free(&o->send_sink);
-}
-
-void init_receiving (DatagramPeerIO *o)
-{
-    // init source
-    DatagramSocketSource_Init(&o->recv_source, FlowErrorReporter_Create(&o->domain, DATAGRAMPEERIO_COMPONENT_SOURCE), &o->sock, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor));
+    // free dgram send interface
+    BDatagram_SendAsync_Free(&o->dgram);
     
-    // connect source
-    PacketRecvConnector_ConnectInput(&o->recv_connector, DatagramSocketSource_GetOutput(&o->recv_source));
-}
-
-void free_receiving (DatagramPeerIO *o)
-{
     // disconnect source
     PacketRecvConnector_DisconnectInput(&o->recv_connector);
     
-    // free source
-    DatagramSocketSource_Free(&o->recv_source);
+    // free dgram recv interface
+    BDatagram_RecvAsync_Free(&o->dgram);
 }
 
-void error_handler (DatagramPeerIO *o, int component, int code)
+void dgram_handler (DatagramPeerIO *o, int event)
 {
-    ASSERT(o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND)
     DebugObject_Access(&o->d_obj);
+    ASSERT(o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND)
     
     BLog(BLOG_NOTICE, "error");
+    
+    // reset mode
+    reset_mode(o);
+    
+    // report error
+    if (o->handler_error) {
+        o->handler_error(o->user);
+        return;
+    }
 }
 
 void reset_mode (DatagramPeerIO *o)
@@ -96,19 +97,14 @@ void reset_mode (DatagramPeerIO *o)
         return;
     }
     
-    // free sending
-    if (o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->bind_sending_up) {
-        free_sending(o);
-    }
-    
     // remove recv notifier handler
     PacketPassNotifier_SetHandler(&o->recv_notifier, NULL, NULL);
     
-    // free receiving
-    free_receiving(o);
+    // free I/O
+    free_io(o);
     
-    // free socket
-    BSocket_Free(&o->sock);
+    // free datagram object
+    BDatagram_Free(&o->dgram);
     
     // set mode
     o->mode = DATAGRAMPEERIO_MODE_NONE;
@@ -122,18 +118,16 @@ void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_l
     // obtain addresses from last received packet
     BAddr addr;
     BIPAddr local_addr;
-    DatagramSocketSource_GetLastAddresses(&o->recv_source, &addr, &local_addr);
-    
-    if (!o->bind_sending_up) {
-        // init sending
-        init_sending(o, addr, local_addr);
-        
-        // set sending up
-        o->bind_sending_up = 1;
-    } else {
-        // update addresses
-        DatagramSocketSink_SetAddresses(&o->send_sink, addr, local_addr);
+    ASSERT_EXECUTE(BDatagram_GetLastReceiveAddrs(&o->dgram, &addr, &local_addr))
+    
+    // check address family just in case
+    if (!BDatagram_AddressFamilySupported(addr.type)) {
+        BLog(BLOG_ERROR, "unsupported receive address");
+        return;
     }
+    
+    // update addresses
+    BDatagram_SetSendAddrs(&o->dgram, addr, local_addr);
 }
 
 int DatagramPeerIO_Init (
@@ -164,6 +158,9 @@ int DatagramPeerIO_Init (
     o->payload_mtu = payload_mtu;
     o->sp_params = sp_params;
     
+    // set no handlers
+    o->handler_error = NULL;
+    
     // check payload MTU (for FragmentProto)
     if (o->payload_mtu > UINT16_MAX) {
         BLog(BLOG_ERROR, "payload MTU is too big");
@@ -182,9 +179,6 @@ int DatagramPeerIO_Init (
         goto fail0;
     }
     
-    // init error domain
-    FlowErrorDomain_Init(&o->domain, (FlowErrorDomain_handler)error_handler, o);
-    
     // init receiving
     
     // init assembler
@@ -284,69 +278,57 @@ PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o)
 
 int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr)
 {
-    ASSERT(!BAddr_IsInvalid(&addr))
     DebugObject_Access(&o->d_obj);
+    ASSERT(BDatagram_AddressFamilySupported(addr.type))
     
     // reset mode
     reset_mode(o);
     
-    // init socket
-    if (BSocket_Init(&o->sock, o->reactor, addr.type, BSOCKET_TYPE_DGRAM) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Init failed");
-        goto fail1;
-    }
-    
-    // connect the socket
-    // Windows needs this or receive will fail; however, FreeBSD will refuse to send
-    // if this is done
-    #ifdef BADVPN_USE_WINAPI
-    if (BSocket_Connect(&o->sock, &addr, 0) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Connect failed");
-        goto fail2;
+    // init dgram
+    if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) {
+        BLog(BLOG_ERROR, "BDatagram_Init failed");
+        goto fail0;
     }
-    #endif
-    
-    // init receiving
-    init_receiving(o);
     
-    // init sending
+    // set send address
     BIPAddr local_addr;
     BIPAddr_InitInvalid(&local_addr);
-    init_sending(o, addr, local_addr);
+    BDatagram_SetSendAddrs(&o->dgram, addr, local_addr);
+    
+    // init I/O
+    init_io(o);
     
     // set mode
     o->mode = DATAGRAMPEERIO_MODE_CONNECT;
     
     return 1;
     
-fail2:
-    BSocket_Free(&o->sock);
-fail1:
+fail0:
     return 0;
 }
 
 int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr)
 {
-    ASSERT(!BAddr_IsInvalid(&addr))
     DebugObject_Access(&o->d_obj);
+    ASSERT(BDatagram_AddressFamilySupported(addr.type))
     
     // reset mode
     reset_mode(o);
     
-    // init socket
-    if (BSocket_Init(&o->sock, o->reactor, addr.type, BSOCKET_TYPE_DGRAM) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Init failed");
-        goto fail1;
+    // init dgram
+    if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) {
+        BLog(BLOG_ERROR, "BDatagram_Init failed");
+        goto fail0;
     }
     
-    // bind socket
-    if (BSocket_Bind(&o->sock, &addr) < 0) {
-        BLog(BLOG_INFO, "BSocket_Bind failed");
-        goto fail2;
+    // bind dgram
+    if (!BDatagram_Bind(&o->dgram, addr)) {
+        BLog(BLOG_INFO, "BDatagram_Bind failed");
+        goto fail1;
     }
     
-    // init receiving
-    init_receiving(o);
+    // init I/O
+    init_io(o);
     
     // set recv notifier handler
     PacketPassNotifier_SetHandler(&o->recv_notifier, (PacketPassNotifier_handler_notify)recv_decoder_notifier_handler, o);
@@ -354,14 +336,11 @@ int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr)
     // set mode
     o->mode = DATAGRAMPEERIO_MODE_BIND;
     
-    // set sending not up
-    o->bind_sending_up = 0;
-    
     return 1;
     
-fail2:
-    BSocket_Free(&o->sock);
 fail1:
+    BDatagram_Free(&o->dgram);
+fail0:
     return 0;
 }
 
@@ -425,10 +404,16 @@ void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o)
     SPProtoDecoder_RemoveOTPSeeds(&o->recv_decoder);
 }
 
-void DatagramPeerIO_SetHandlers (DatagramPeerIO *o, DatagramPeerIO_handler_otp_warning handler_otp_warning, DatagramPeerIO_handler_otp_ready handler_otp_ready, void *user)
+void DatagramPeerIO_SetHandlers (DatagramPeerIO *o, void *user,
+                                 DatagramPeerIO_handler_error handler_error,
+                                 DatagramPeerIO_handler_otp_warning handler_otp_warning,
+                                 DatagramPeerIO_handler_otp_ready handler_otp_ready)
 {
     DebugObject_Access(&o->d_obj);
     
+    o->user = user;
+    o->handler_error = handler_error;
+    
     SPProtoDecoder_SetHandlers(&o->recv_decoder, handler_otp_ready, user);
     SPProtoEncoder_SetHandlers(&o->send_encoder, handler_otp_warning, user);
 }

+ 26 - 24
client/DatagramPeerIO.h

@@ -35,33 +35,40 @@
 #include <base/DebugObject.h>
 #include <system/BReactor.h>
 #include <system/BAddr.h>
-#include <system/BSocket.h>
+#include <system/BDatagram.h>
 #include <system/BTime.h>
 #include <flow/PacketPassInterface.h>
 #include <flow/PacketPassConnector.h>
 #include <flow/SinglePacketBuffer.h>
 #include <flow/PacketRecvConnector.h>
 #include <flow/PacketPassNotifier.h>
-#include <flowextra/DatagramSocketSource.h>
-#include <flowextra/DatagramSocketSink.h>
 #include <client/FragmentProtoDisassembler.h>
 #include <client/FragmentProtoAssembler.h>
 #include <client/SPProtoEncoder.h>
 #include <client/SPProtoDecoder.h>
 
+/**
+ * Callback function invoked when an error occurs with the peer connection.
+ * The object has entered default state.
+ * May be called from within a sending Send call.
+ *
+ * @param user as in {@link DatagramPeerIO_SetHandlers}
+ */
+typedef void (*DatagramPeerIO_handler_error) (void *user);
+
 /**
  * Handler function invoked when the number of used OTPs has reached
  * the specified warning number in {@link DatagramPeerIO_SetOTPWarningHandler}.
  * May be called from within a sending Send call.
  *
- * @param user as in {@link DatagramPeerIO_SetOTPWarningHandler}
+ * @param user as in {@link DatagramPeerIO_SetHandlers}
  */
 typedef void (*DatagramPeerIO_handler_otp_warning) (void *user);
 
 /**
  * Handler called when OTP generation for a new receive seed is finished.
  * 
- * @param user as in {@link DatagramPeerIO_Init}
+ * @param user as in {@link DatagramPeerIO_SetHandlers}
  */
 typedef void (*DatagramPeerIO_handler_otp_ready) (void *user);
 
@@ -86,14 +93,11 @@ typedef struct {
     BReactor *reactor;
     int payload_mtu;
     struct spproto_security_params sp_params;
+    void *user;
+    DatagramPeerIO_handler_error handler_error;
     int spproto_payload_mtu;
     int effective_socket_mtu;
     
-    // flow error domain
-    FlowErrorDomain domain;
-    
-    // persistent I/O objects
-    
     // sending base
     FragmentProtoDisassembler send_disassembler;
     SPProtoEncoder send_encoder;
@@ -110,23 +114,15 @@ typedef struct {
     // mode
     int mode;
     
-    // in binded mode, whether sending is up
-    int bind_sending_up;
-    
-    // datagram socket
-    BSocket sock;
-    
-    // non-persistent sending objects
-    DatagramSocketSink send_sink;
-    
-    // non-persistent receiving objects
-    DatagramSocketSource recv_source;
+    // datagram object
+    BDatagram dgram;
 } DatagramPeerIO;
 
 /**
  * Initializes the object.
  * The interface is initialized in default mode.
  * {@link BLog_Init} must have been done.
+ * {@link BNetwork_GlobalInit} must have been done.
  * {@link BSecurity_GlobalInitThreadSafe} must have been done if
  * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1.
  *
@@ -181,7 +177,8 @@ PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o);
  * On failure, the interface enters default mode.
  *
  * @param o the object
- * @param addr address to send packets to. Must be recognized and not invalid.
+ * @param addr address to send packets to. Must be supported according to
+ *             {@link BDatagram_AddressFamilySupported}.
  * @return 1 on success, 0 on failure
  */
 int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) WARN_UNUSED;
@@ -192,7 +189,8 @@ int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) WARN_UNUSED;
  * On failure, the interface enters default mode.
  *
  * @param o the object
- * @param addr address to bind to. Must be recognized and not invalid.
+ * @param addr address to bind to. Must be supported according to
+ *             {@link BDatagram_AddressFamilySupported}.
  * @return 1 on success, 0 on failure
  */
 int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) WARN_UNUSED;
@@ -256,10 +254,14 @@ void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o);
  * Sets handlers.
  * 
  * @param o the object
+ * @param handler_error error handler
  * @param handler_otp_warning OTP warning handler
  * @param handler_otp_ready handler called when OTP generation for a new receive seed is finished
  * @param user value to pass to handler
  */
-void DatagramPeerIO_SetHandlers (DatagramPeerIO *o, DatagramPeerIO_handler_otp_warning handler_otp_warning, DatagramPeerIO_handler_otp_ready handler_otp_ready, void *user);
+void DatagramPeerIO_SetHandlers (DatagramPeerIO *o, void *user,
+                                 DatagramPeerIO_handler_error handler_error,
+                                 DatagramPeerIO_handler_otp_warning handler_otp_warning,
+                                 DatagramPeerIO_handler_otp_ready handler_otp_ready);
 
 #endif

+ 123 - 137
client/PasswordListener.c

@@ -22,8 +22,6 @@
 
 #include <stdlib.h>
 
-#include <openssl/rand.h>
-
 #include <prerror.h>
 
 #include <ssl.h>
@@ -31,20 +29,21 @@
 #include <misc/debug.h>
 #include <misc/offset.h>
 #include <misc/byteorder.h>
+#include <misc/balloc.h>
 #include <base/BLog.h>
+#include <security/BRandom.h>
 #include <nspr_support/DummyPRFileDesc.h>
-#include <nspr_support/BSocketPRFileDesc.h>
 
 #include <client/PasswordListener.h>
 
 #include <generated/blog_channel_PasswordListener.h>
 
 static int password_comparator (void *user, uint64_t *p1, uint64_t *p2);
-static void cleanup_client (PasswordListener *l, struct PasswordListenerClient *client);
+static void remove_client (struct PasswordListenerClient *client);
 static void listener_handler (PasswordListener *l);
-static void client_try_read (struct PasswordListenerClient *client);
-static void client_read_handler (struct PasswordListenerClient *client, int event);
-static void client_read_handler_ssl (struct PasswordListenerClient *client, PRInt16 event);
+static void client_connection_handler (struct PasswordListenerClient *client, int event);
+static void client_sslcon_handler (struct PasswordListenerClient *client, int event);
+static void client_receiver_handler (struct PasswordListenerClient *client);
 
 int password_comparator (void *user, uint64_t *p1, uint64_t *p2)
 {
@@ -57,50 +56,76 @@ int password_comparator (void *user, uint64_t *p1, uint64_t *p2)
     return 0;
 }
 
-void cleanup_client (PasswordListener *l, struct PasswordListenerClient *client)
+void remove_client (struct PasswordListenerClient *client)
 {
+    PasswordListener *l = client->l;
+    
+    // free receiver
+    SingleStreamReceiver_Free(&client->receiver);
+    
+    // free SSL
     if (l->ssl) {
-        BPRFileDesc_Free(&client->sock->ssl_bprfd);
+        BSSLConnection_Free(&client->sslcon);
         ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS)
     }
-    BSocket_Free(&client->sock->sock);
+    
+    // free connection interfaces
+    BConnection_RecvAsync_Free(&client->sock->con);
+    BConnection_SendAsync_Free(&client->sock->con);
+    
+    // free connection
+    BConnection_Free(&client->sock->con);
+    
+    // free sslsocket structure
     free(client->sock);
+    
+    // move to free list
+    LinkedList2_Remove(&l->clients_used, &client->list_node);
+    LinkedList2_Append(&l->clients_free, &client->list_node);
 }
 
 void listener_handler (PasswordListener *l)
 {
-    // grab client entry
-    LinkedList2Node *node;
-    struct PasswordListenerClient *client;
-    if (node = LinkedList2_GetFirst(&l->clients_free)) {
-        client = UPPER_OBJECT(node, struct PasswordListenerClient, list_node);
-        LinkedList2_Remove(&l->clients_free, &client->list_node);
-    } else {
-        node = LinkedList2_GetFirst(&l->clients_used);
-        ASSERT(node)
-        client = UPPER_OBJECT(node, struct PasswordListenerClient, list_node);
-        cleanup_client(l, client);
-        LinkedList2_Remove(&l->clients_used, &client->list_node);
+    DebugObject_Access(&l->d_obj);
+    
+    // obtain client entry
+    if (LinkedList2_IsEmpty(&l->clients_free)) {
+        struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList2_GetFirst(&l->clients_used), struct PasswordListenerClient, list_node);
+        remove_client(client);
     }
+    struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList2_GetLast(&l->clients_free), struct PasswordListenerClient, list_node);
+    LinkedList2_Remove(&l->clients_free, &client->list_node);
+    LinkedList2_Append(&l->clients_used, &client->list_node);
     
-    if (!(client->sock = malloc(sizeof(sslsocket)))) {
-        BLog(BLOG_ERROR, "cannot allocate sslsocket");
+    // allocate sslsocket structure
+    if (!(client->sock = malloc(sizeof(*client->sock)))) {
+        BLog(BLOG_ERROR, "malloc failedt");
         goto fail0;
     }
     
-    // accept a client
-    if (!Listener_Accept(&l->listener, &client->sock->sock, NULL)) {
-        BLog(BLOG_ERROR, "Listener_Accept failed");
+    // accept connection
+    if (!BConnection_Init(&client->sock->con, BCONNECTION_SOURCE_LISTENER(&l->listener, NULL), l->bsys, client, (BConnection_handler)client_connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
         goto fail1;
     }
     
     BLog(BLOG_INFO, "Connection accepted");
     
+    // init connection interfaces
+    BConnection_SendAsync_Init(&client->sock->con);
+    BConnection_RecvAsync_Init(&client->sock->con);
+    
+    StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->sock->con);
+    StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->sock->con);
+    
     if (l->ssl) {
-        // create BSocket NSPR file descriptor
-        BSocketPRFileDesc_Create(&client->sock->bottom_prfd, &client->sock->sock);
+        // create bottom NSPR file descriptor
+        if (!BSSLConnection_MakeBackend(&client->sock->bottom_prfd, send_if, recv_if)) {
+            BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed");
+            goto fail2;
+        }
         
-        // create SSL file descriptor from the socket's BSocketPRFileDesc
+        // create SSL file descriptor from the bottom NSPR file descriptor
         if (!(client->sock->ssl_prfd = SSL_ImportFD(l->model_prfd, &client->sock->bottom_prfd))) {
             ASSERT_FORCE(PR_Close(&client->sock->bottom_prfd) == PR_SUCCESS)
             goto fail2;
@@ -122,25 +147,16 @@ void listener_handler (PasswordListener *l)
             goto fail3;
         }
         
-        // initialize BPRFileDesc on SSL file descriptor
-        BPRFileDesc_Init(&client->sock->ssl_bprfd, client->sock->ssl_prfd);
+        // initialize SSLConnection
+        BSSLConnection_Init(&client->sslcon, client->sock->ssl_prfd, 0, l->bsys, client, (BSSLConnection_handler)client_sslcon_handler);
         
-        // set read handler
-        BPRFileDesc_AddEventHandler(&client->sock->ssl_bprfd, PR_POLL_READ, (BPRFileDesc_handler)client_read_handler_ssl, client);
-    } else {
-        // set read handler
-        BSocket_AddEventHandler(&client->sock->sock, BSOCKET_READ, (BSocket_handler)client_read_handler, client);
+        send_if = BSSLConnection_GetSendIf(&client->sslcon);
+        recv_if = BSSLConnection_GetRecvIf(&client->sslcon);
     }
     
-    // init buffer
-    client->recv_buffer_pos = 0;
+    // init receiver
+    SingleStreamReceiver_Init(&client->receiver, (uint8_t *)&client->recv_buffer, sizeof(client->recv_buffer), recv_if, BReactor_PendingGroup(l->bsys), client, (SingleStreamReceiver_handler)client_receiver_handler);
     
-    // add to used list
-    LinkedList2_Append(&l->clients_used, &client->list_node);
-    
-    // start receiving password
-    // NOTE: listener and connection can die
-    client_try_read(client);
     return;
     
     // cleanup on error
@@ -149,69 +165,54 @@ fail3:
         ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS)
     }
 fail2:
-    BSocket_Free(&client->sock->sock);
+    BConnection_RecvAsync_Free(&client->sock->con);
+    BConnection_SendAsync_Free(&client->sock->con);
+    BConnection_Free(&client->sock->con);
 fail1:
     free(client->sock);
 fail0:
+    LinkedList2_Remove(&l->clients_used, &client->list_node);
     LinkedList2_Append(&l->clients_free, &client->list_node);
 }
 
-void client_try_read (struct PasswordListenerClient *client)
+void client_connection_handler (struct PasswordListenerClient *client, int event)
 {
     PasswordListener *l = client->l;
+    DebugObject_Access(&l->d_obj);
     
-    if (l->ssl) {
-        while (client->recv_buffer_pos < sizeof(client->recv_buffer)) {
-            PRInt32 recvd = PR_Read(
-                client->sock->ssl_prfd,
-                (uint8_t *)&client->recv_buffer + client->recv_buffer_pos,
-                sizeof(client->recv_buffer) - client->recv_buffer_pos
-            );
-            if (recvd < 0) {
-                PRErrorCode error = PR_GetError();
-                if (error == PR_WOULD_BLOCK_ERROR) {
-                    BPRFileDesc_EnableEvent(&client->sock->ssl_bprfd, PR_POLL_READ);
-                    return;
-                }
-                BLog(BLOG_ERROR, "PR_Read failed (%d)", (int)error);
-                goto free_client;
-            }
-            if (recvd == 0) {
-                BLog(BLOG_INFO, "Connection terminated");
-                goto free_client;
-            }
-            client->recv_buffer_pos += recvd;
-        }
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
+        BLog(BLOG_INFO, "connection closed");
     } else {
-        while (client->recv_buffer_pos < sizeof(client->recv_buffer)) {
-            int recvd = BSocket_Recv(
-                &client->sock->sock,
-                (uint8_t *)&client->recv_buffer + client->recv_buffer_pos,
-                sizeof(client->recv_buffer) - client->recv_buffer_pos
-            );
-            if (recvd < 0) {
-                int error = BSocket_GetError(&client->sock->sock);
-                if (error == BSOCKET_ERROR_LATER) {
-                    BSocket_EnableEvent(&client->sock->sock, BSOCKET_READ);
-                    return;
-                }
-                BLog(BLOG_ERROR, "BSocket_Recv failed (%d)", error);
-                goto free_client;
-            }
-            if (recvd == 0) {
-                BLog(BLOG_INFO, "Connection terminated");
-                goto free_client;
-            }
-            client->recv_buffer_pos += recvd;
-        }
+        BLog(BLOG_INFO, "connection error");
     }
     
+    remove_client(client);
+}
+
+void client_sslcon_handler (struct PasswordListenerClient *client, int event)
+{
+    PasswordListener *l = client->l;
+    DebugObject_Access(&l->d_obj);
+    ASSERT(l->ssl)
+    ASSERT(event == BSSLCONNECTION_EVENT_ERROR)
+    
+    BLog(BLOG_INFO, "SSL error");
+    
+    remove_client(client);
+}
+
+void client_receiver_handler (struct PasswordListenerClient *client)
+{
+    PasswordListener *l = client->l;
+    DebugObject_Access(&l->d_obj);
+    
     // check password
     uint64_t received_pass = ltoh64(client->recv_buffer);
     BAVLNode *pw_tree_node = BAVL_LookupExact(&l->passwords, &received_pass);
     if (!pw_tree_node) {
         BLog(BLOG_WARNING, "unknown password");
-        goto free_client;
+        remove_client(client);
+        return;
     }
     PasswordListener_pwentry *pw_entry = UPPER_OBJECT(pw_tree_node, PasswordListener_pwentry, tree_node);
     
@@ -220,57 +221,43 @@ void client_try_read (struct PasswordListenerClient *client)
     // remove password entry
     BAVL_Remove(&l->passwords, &pw_entry->tree_node);
     
-    // move client entry to free list
-    LinkedList2_Remove(&l->clients_used, &client->list_node);
-    LinkedList2_Append(&l->clients_free, &client->list_node);
+    // free receiver
+    SingleStreamReceiver_Free(&client->receiver);
     
     if (l->ssl) {
-        // remove event handler
-        BPRFileDesc_RemoveEventHandler(&client->sock->ssl_bprfd, PR_POLL_READ);
+        // free SSL connection
+        BSSLConnection_Free(&client->sslcon);
     } else {
-        // remove event handler
-        BSocket_RemoveEventHandler(&client->sock->sock, BSOCKET_READ);
+        // free connection interfaces
+        BConnection_RecvAsync_Free(&client->sock->con);
+        BConnection_SendAsync_Free(&client->sock->con);
     }
     
-    // give the socket to the handler
-    // NOTE: listener can die
-    pw_entry->handler_client(pw_entry->user, client->sock);
-    return;
+    // remove connection handler
+    BConnection_SetHandlers(&client->sock->con, NULL, NULL);
     
-free_client:
-    cleanup_client(l, client);
+    // move client entry to free list
     LinkedList2_Remove(&l->clients_used, &client->list_node);
     LinkedList2_Append(&l->clients_free, &client->list_node);
-}
-
-void client_read_handler (struct PasswordListenerClient *client, int event)
-{
-    ASSERT(event == BSOCKET_READ)
-    BSocket_DisableEvent(&client->sock->sock, BSOCKET_READ);
-    
-    // NOTE: listener and connection can die
-    client_try_read(client);
-}
-
-void client_read_handler_ssl (struct PasswordListenerClient *client, PRInt16 event)
-{
-    ASSERT(event == PR_POLL_READ)
     
-    // NOTE: listener and connection can die
-    client_try_read(client);
+    // give the socket to the handler
+    pw_entry->handler_client(pw_entry->user, client->sock);
+    return;
 }
 
 int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BAddr listen_addr, int max_clients, int ssl, CERTCertificate *cert, SECKEYPrivateKey *key)
 {
-    ASSERT(!BAddr_IsInvalid(&listen_addr))
+    ASSERT(BConnection_AddressSupported(listen_addr))
     ASSERT(max_clients > 0)
     ASSERT(ssl == 0 || ssl == 1)
     
+    // init arguments
     l->bsys = bsys;
     l->ssl = ssl;
     
     // allocate client entries
-    if (!(l->clients_data = malloc(max_clients * sizeof(struct PasswordListenerClient)))) {
+    if (!(l->clients_data = BAllocArray(max_clients, sizeof(struct PasswordListenerClient)))) {
+        BLog(BLOG_ERROR, "BAllocArray failed");
         goto fail0;
     }
     
@@ -293,8 +280,7 @@ int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BAddr listen_add
     // initialize client entries
     LinkedList2_Init(&l->clients_free);
     LinkedList2_Init(&l->clients_used);
-    int i;
-    for (i = 0; i < max_clients; i++) {
+    for (int i = 0; i < max_clients; i++) {
         struct PasswordListenerClient *conn = &l->clients_data[i];
         conn->l = l;
         LinkedList2_Append(&l->clients_free, &conn->list_node);
@@ -304,14 +290,12 @@ int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BAddr listen_add
     BAVL_Init(&l->passwords, OFFSET_DIFF(PasswordListener_pwentry, password, tree_node), (BAVL_comparator)password_comparator, NULL);
     
     // initialize listener
-    if (!Listener_Init(&l->listener, l->bsys, listen_addr, (Listener_handler)listener_handler, l)) {
+    if (!BListener_Init(&l->listener, listen_addr,  l->bsys, l, (BListener_handler)listener_handler)) {
         BLog(BLOG_ERROR, "Listener_Init failed");
         goto fail2;
     }
     
-    // init debug object
     DebugObject_Init(&l->d_obj);
-    
     return 1;
     
     // cleanup
@@ -320,27 +304,24 @@ fail2:
         ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS)
     }
 fail1:
-    free(l->clients_data);
+    BFree(l->clients_data);
 fail0:
     return 0;
 }
 
 void PasswordListener_Free (PasswordListener *l)
 {
-    // free debug object
     DebugObject_Free(&l->d_obj);
 
     // free clients
-    LinkedList2Iterator it;
-    LinkedList2Iterator_InitForward(&it, &l->clients_used);
     LinkedList2Node *node;
-    while (node = LinkedList2Iterator_Next(&it)) {
+    while (node = LinkedList2_GetFirst(&l->clients_used)) {
         struct PasswordListenerClient *client = UPPER_OBJECT(node, struct PasswordListenerClient, list_node);
-        cleanup_client(l, client);
+        remove_client(client);
     }
     
     // free listener
-    Listener_Free(&l->listener);
+    BListener_Free(&l->listener);
     
     // free model SSL file descriptor
     if (l->ssl) {
@@ -348,15 +329,17 @@ void PasswordListener_Free (PasswordListener *l)
     }
     
     // free client entries
-    free(l->clients_data);
+    BFree(l->clients_data);
 }
 
 uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user)
 {
+    DebugObject_Access(&l->d_obj);
+    
     while (1) {
         // generate password
-        DEBUG_ZERO_MEMORY(&entry->password, sizeof(entry->password));
-        ASSERT_FORCE(RAND_bytes((uint8_t *)&entry->password, sizeof(entry->password)) == 1)
+        BRandom_randomize((uint8_t *)&entry->password, sizeof(entry->password));
+        
         // try inserting
         if (BAVL_Insert(&l->passwords, &entry->tree_node, NULL)) {
             break;
@@ -371,5 +354,8 @@ uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentr
 
 void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry)
 {
+    DebugObject_Access(&l->d_obj);
+    
+    // remove
     BAVL_Remove(&l->passwords, &entry->tree_node);
 }

+ 13 - 11
client/PasswordListener.h

@@ -36,12 +36,13 @@
 #include <keyhi.h>
 
 #include <misc/debug.h>
-#include <base/DebugObject.h>
-#include <system/Listener.h>
 #include <misc/sslsocket.h>
 #include <structure/LinkedList2.h>
 #include <structure/BAVL.h>
-#include <nspr_support/BPRFileDesc.h>
+#include <base/DebugObject.h>
+#include <flow/SingleStreamReceiver.h>
+#include <system/BConnection.h>
+#include <nspr_support/BSSLConnection.h>
 
 /**
  * Handler function called when a client identifies itself with a password
@@ -50,10 +51,10 @@
  * and must not be unregistered again.
  * 
  * @param user as in {@link PasswordListener_AddEntry}
- * @param sock structure that contains the socket ({@link BSocket}) and, if TLS
- *             is enabled, the SSL socket (PRFileDesc and {@link BPRFileDesc}).
- *             The structure was allocated with malloc() and the user
- *             is responsible for freeing it.
+ * @param sock structure containing a {@link BConnection} and, if TLS is enabled,
+ *             the SSL socket with the bottom layer connected to the async interfaces
+ *             of the {@link BConnection} object. The structure was allocated with
+ *             malloc() and the user is responsible for freeing it.
  */
 typedef void (*PasswordListener_handler_client) (void *user, sslsocket *sock);
 
@@ -64,7 +65,6 @@ struct PasswordListenerClient;
  * based on a number they send.
  */
 typedef struct {
-    DebugObject d_obj;
     BReactor *bsys;
     int ssl;
     PRFileDesc model_dprfd;
@@ -73,7 +73,8 @@ typedef struct {
     LinkedList2 clients_free;
     LinkedList2 clients_used;
     BAVL passwords;
-    Listener listener;
+    BListener listener;
+    DebugObject d_obj;
 } PasswordListener;
 
 typedef struct {
@@ -87,8 +88,9 @@ struct PasswordListenerClient {
     PasswordListener *l;
     LinkedList2Node list_node;
     sslsocket *sock;
+    BSSLConnection sslcon;
+    SingleStreamReceiver receiver;
     uint64_t recv_buffer;
-    int recv_buffer_pos;
 };
 
 /**
@@ -96,7 +98,7 @@ struct PasswordListenerClient {
  * 
  * @param l the object
  * @param bsys reactor we live in
- * @param listen_addr address to listen on. Must not be invalid.
+ * @param listen_addr address to listen on. Must be supported according to {@link BConnection_AddressSupported}.
  * @param max_clients maximum number of client to hold until they are identified.
  *                    Must be >0.
  * @param ssl whether to use TLS. Must be 1 or 0.

+ 0 - 106
client/PasswordSender.c

@@ -1,106 +0,0 @@
-/**
- * @file PasswordSender.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/debug.h>
-
-#include <client/PasswordSender.h>
-
-#define COMPONENT_SINK 1
-
-static void call_handler (PasswordSender *o, int is_error)
-{
-    DEBUGERROR(&o->d_err, o->handler(o->user, is_error))
-}
-
-static void error_handler (PasswordSender *o, int component, int code)
-{
-    ASSERT(component == COMPONENT_SINK)
-    DebugObject_Access(&o->d_obj);
-    
-    call_handler(o, 1);
-    return;
-}
-
-static void sent_handler (PasswordSender *o)
-{
-    DebugObject_Access(&o->d_obj);
-    
-    call_handler(o, 0);
-    return;
-}
-
-void PasswordSender_Init (PasswordSender *o, uint64_t password, int ssl, BSocket *plain_sock, BPRFileDesc *ssl_bprfd, PasswordSender_handler handler, void *user, BReactor *reactor)
-{
-    ASSERT(ssl == 0 || ssl == 1)
-    
-    // init arguments
-    o->password = password;
-    o->ssl = ssl;
-    if (ssl) {
-        o->ssl_bprfd = ssl_bprfd;
-    } else {
-        o->plain_sock = plain_sock;
-    }
-    o->handler = handler;
-    o->user = user;
-    
-    // init error handler
-    FlowErrorDomain_Init(&o->domain, (FlowErrorDomain_handler)error_handler, o);
-    
-    // init sink
-    StreamPassInterface *sink_if;
-    if (o->ssl) {
-        PRStreamSink_Init(&o->sink.ssl, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), o->ssl_bprfd, BReactor_PendingGroup(reactor));
-        sink_if = PRStreamSink_GetInput(&o->sink.ssl);
-    } else {
-        StreamSocketSink_Init(&o->sink.plain, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), o->plain_sock, BReactor_PendingGroup(reactor));
-        sink_if = StreamSocketSink_GetInput(&o->sink.plain);
-    }
-    
-    // init PacketStreamSender
-    PacketStreamSender_Init(&o->pss, sink_if, sizeof(o->password), BReactor_PendingGroup(reactor));
-    
-    // init SinglePacketSender
-    SinglePacketSender_Init(&o->sps, (uint8_t *)&o->password, sizeof(o->password), PacketStreamSender_GetInput(&o->pss), (SinglePacketSender_handler)sent_handler, o, BReactor_PendingGroup(reactor));
-    
-    DebugObject_Init(&o->d_obj);
-    DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
-}
-
-void PasswordSender_Free (PasswordSender *o)
-{
-    DebugError_Free(&o->d_err);
-    DebugObject_Free(&o->d_obj);
-    
-    // free SinglePacketSender
-    SinglePacketSender_Free(&o->sps);
-    
-    // free PacketStreamSender
-    PacketStreamSender_Free(&o->pss);
-    
-    // free sink
-    if (o->ssl) {
-        PRStreamSink_Free(&o->sink.ssl);
-    } else {
-        StreamSocketSink_Free(&o->sink.plain);
-    }
-}

+ 0 - 99
client/PasswordSender.h

@@ -1,99 +0,0 @@
-/**
- * @file PasswordSender.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * Object used to send a password to a {@link PasswordListener} server.
- */
-
-#ifndef BADVPN_CLIENT_PASSWORDSENDER_H
-#define BADVPN_CLIENT_PASSWORDSENDER_H
-
-#include <stdint.h>
-
-#include <misc/debugerror.h>
-#include <system/BSocket.h>
-#include <base/DebugObject.h>
-#include <flow/SinglePacketSender.h>
-#include <flow/PacketStreamSender.h>
-#include <flowextra/StreamSocketSink.h>
-#include <nspr_support/BPRFileDesc.h>
-#include <nspr_support/PRStreamSink.h>
-
-/**
- * Handler function called when the password is sent, or an error occurs
- * on the socket.
- * The object must be freed from within this handler.
- * 
- * @param user as in {@link PasswordSender_Init}
- * @param is_error whether the password was sent successfuly, or an error
- *                 occured on the socket. 0 means password sent, 1 means error.
- */
-typedef void (*PasswordSender_handler) (void *user, int is_error);
-
-/**
- * Object used to send a password to a {@link PasswordListener} server.
- */
-typedef struct {
-    uint64_t password;
-    int ssl;
-    union {
-        BSocket *plain_sock;
-        BPRFileDesc *ssl_bprfd;
-    };
-    PasswordSender_handler handler;
-    void *user;
-    FlowErrorDomain domain;
-    SinglePacketSender sps;
-    PacketStreamSender pss;
-    union {
-        StreamSocketSink plain;
-        PRStreamSink ssl;
-    } sink;
-    DebugObject d_obj;
-    DebugError d_err;
-} PasswordSender;
-
-/**
- * Initializes the object.
- * 
- * @param o the object
- * @param password password to send
- * @param ssl whether we are connected to the server using TLS. Must be 1 or 0.
- * @param plain_sock if not using TLS, the socket to send the password through. Nothing else
- *                   must be using this socket for sending.
- * @param ssl_bprfd if using TLS, the {@link BPRFileDesc} object for the SSL file descriptor
- *                  to send the password through. Nothing else must be using this SSL socket
- *                  for sending.
- * @param handler handler to call when the password is sent or an error occurs
- * @param user value to pass to handler
- * @param reactor reactor we live in
- */
-void PasswordSender_Init (PasswordSender *o, uint64_t password, int ssl, BSocket *plain_sock, BPRFileDesc *ssl_bprfd, PasswordSender_handler handler, void *user, BReactor *reactor);
-
-/**
- * Frees the object.
- * 
- * @param o the object
- */
-void PasswordSender_Free (PasswordSender *o);
-
-#endif

+ 255 - 280
client/StreamPeerIO.c

@@ -34,10 +34,6 @@
 
 #include <generated/blog_channel_StreamPeerIO.h>
 
-#define STREAMPEERIO_COMPONENT_SEND_SINK 0
-#define STREAMPEERIO_COMPONENT_RECEIVE_SOURCE 1
-#define STREAMPEERIO_COMPONENT_RECEIVE_DECODER 2
-
 #define MODE_NONE 0
 #define MODE_CONNECT 1
 #define MODE_LISTEN 2
@@ -52,185 +48,150 @@
 #define LISTEN_STATE_GOTCLIENT 1
 #define LISTEN_STATE_FINISHED 2
 
-#define COMPONENT_SOURCE 1
-#define COMPONENT_SINK 2
-#define COMPONENT_DECODER 3
-
-static int init_persistent_io (StreamPeerIO *pio, PacketPassInterface *user_recv_if);
-static void free_persistent_io (StreamPeerIO *pio);
-static void connecting_connect_handler (StreamPeerIO *pio, int event);
-static SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer);
-static SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey);
-static void connecting_try_handshake (StreamPeerIO *pio);
-static void connecting_handshake_read_handler (StreamPeerIO *pio, PRInt16 event);
-static void connecting_pwsender_handler (StreamPeerIO *pio, int is_error);
-static void error_handler (StreamPeerIO *pio, int component, int code);
+static void decoder_handler (StreamPeerIO *pio, int component, int code);
+static void connector_handler (StreamPeerIO *pio, int is_error);
+static void connection_handler (StreamPeerIO *pio, int event);
+static void connect_sslcon_handler (StreamPeerIO *pio, int event);
+static void pwsender_handler (StreamPeerIO *pio);
 static void listener_handler_client (StreamPeerIO *pio, sslsocket *sock);
 static int init_io (StreamPeerIO *pio, sslsocket *sock);
 static void free_io (StreamPeerIO *pio);
+static void sslcon_handler (StreamPeerIO *pio, int event);
+static SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer);
+static SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey);
 static int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert);
 static void reset_state (StreamPeerIO *pio);
-static void cleanup_socket (sslsocket *sock, int ssl);
 static void reset_and_report_error (StreamPeerIO *pio);
 
-void connecting_connect_handler (StreamPeerIO *pio, int event)
+void decoder_handler (StreamPeerIO *pio, int component, int code)
 {
-    ASSERT(event == BSOCKET_CONNECT)
-    ASSERT(pio->mode == MODE_CONNECT)
-    ASSERT(pio->connect.state == CONNECT_STATE_CONNECTING)
     DebugObject_Access(&pio->d_obj);
     
-    // remove connect event handler
-    BSocket_RemoveEventHandler(&pio->connect.sock.sock, BSOCKET_CONNECT);
+    BLog(BLOG_ERROR, "decoder error");
+    
+    reset_and_report_error(pio);
+    return;
+}
+
+void connector_handler (StreamPeerIO *pio, int is_error)
+{
+    DebugObject_Access(&pio->d_obj);
+    ASSERT(pio->mode == MODE_CONNECT)
+    ASSERT(pio->connect.state == CONNECT_STATE_CONNECTING)
     
     // check connection result
-    int res = BSocket_GetConnectResult(&pio->connect.sock.sock);
-    if (res != 0) {
-        BLog(BLOG_NOTICE, "Connection failed (%d)", res);
+    if (is_error) {
+        BLog(BLOG_NOTICE, "connection failed");
+        goto fail0;
+    }
+    
+    // init connection
+    if (!BConnection_Init(&pio->connect.sock.con, BCONNECTION_SOURCE_CONNECTOR(&pio->connect.connector), pio->reactor, pio, (BConnection_handler)connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
         goto fail0;
     }
     
     if (pio->ssl) {
-        // create BSocket NSPR file descriptor
-        BSocketPRFileDesc_Create(&pio->connect.sock.bottom_prfd, &pio->connect.sock.sock);
+        // init connection interfaces
+        BConnection_SendAsync_Init(&pio->connect.sock.con);
+        BConnection_RecvAsync_Init(&pio->connect.sock.con);
+        
+        // create bottom NSPR file descriptor
+        if (!BSSLConnection_MakeBackend(&pio->connect.sock.bottom_prfd, BConnection_SendAsync_GetIf(&pio->connect.sock.con), BConnection_RecvAsync_GetIf(&pio->connect.sock.con))) {
+            BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed");
+            goto fail1;
+        }
         
-        // create SSL file descriptor from the socket's BSocketPRFileDesc
+        // create SSL file descriptor from the bottom NSPR file descriptor
         if (!(pio->connect.sock.ssl_prfd = SSL_ImportFD(NULL, &pio->connect.sock.bottom_prfd))) {
             ASSERT_FORCE(PR_Close(&pio->connect.sock.bottom_prfd) == PR_SUCCESS)
-            goto fail0;
+            goto fail1;
         }
         
         // set client mode
         if (SSL_ResetHandshake(pio->connect.sock.ssl_prfd, PR_FALSE) != SECSuccess) {
             BLog(BLOG_ERROR, "SSL_ResetHandshake failed");
-            goto fail_ssl1;
+            goto fail2;
         }
         
         // set verify peer certificate hook
         if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, (SSLAuthCertificate)client_auth_certificate_callback, pio) != SECSuccess) {
             BLog(BLOG_ERROR, "SSL_AuthCertificateHook failed");
-            goto fail_ssl1;
+            goto fail2;
         }
         
         // set client certificate callback
         if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, (SSLGetClientAuthData)client_client_auth_data_callback, pio) != SECSuccess) {
             BLog(BLOG_ERROR, "SSL_GetClientAuthDataHook failed");
-            goto fail_ssl1;
+            goto fail2;
         }
         
-        // initialize BPRFileDesc on SSL file descriptor
-        BPRFileDesc_Init(&pio->connect.sock.ssl_bprfd, pio->connect.sock.ssl_prfd);
-        
-        // add event handler for driving handshake
-        BPRFileDesc_AddEventHandler(&pio->connect.sock.ssl_bprfd, PR_POLL_READ, (BPRFileDesc_handler)connecting_handshake_read_handler, pio);
+        // init BSSLConnection
+        BSSLConnection_Init(&pio->connect.sslcon, pio->connect.sock.ssl_prfd, 1, pio->reactor, pio, (BSSLConnection_handler)connect_sslcon_handler);
         
         // change state
         pio->connect.state = CONNECT_STATE_HANDSHAKE;
-        
-        // start handshake
-        connecting_try_handshake(pio);
-        return;
     } else {
+        // init connection send interface
+        BConnection_SendAsync_Init(&pio->connect.sock.con);
+        
         // init password sender
-        PasswordSender_Init(&pio->connect.pwsender, pio->connect.password, 0, &pio->connect.sock.sock, NULL, (PasswordSender_handler)connecting_pwsender_handler, pio, pio->reactor);
+        SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BConnection_SendAsync_GetIf(&pio->connect.sock.con), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler);
         
         // change state
         pio->connect.state = CONNECT_STATE_SENDING;
-        
-        return;
     }
     
-    // cleanup
-fail_ssl1:
-    ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS)
+    return;
+
+    if (pio->ssl) {
+fail2:
+        ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS)
+fail1:
+        BConnection_RecvAsync_Free(&pio->connect.sock.con);
+        BConnection_SendAsync_Free(&pio->connect.sock.con);
+    }
+    BConnection_Free(&pio->connect.sock.con);
 fail0:
     reset_and_report_error(pio);
     return;
 }
 
-SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
+void connection_handler (StreamPeerIO *pio, int event)
 {
-    ASSERT(pio->ssl)
-    ASSERT(pio->mode == MODE_CONNECT)
-    ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
     DebugObject_Access(&pio->d_obj);
+    ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN)
+    ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state >= CONNECT_STATE_HANDSHAKE)
+    ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state >= LISTEN_STATE_FINISHED)
     
-    // This callback is used to bypass checking the server's domain name, as peers
-    // don't have domain names. We byte-compare the certificate to the one reported
-    // by the server anyway.
-    
-    SECStatus ret = SECFailure;
-    
-    CERTCertificate *server_cert = SSL_PeerCertificate(pio->connect.sock.ssl_prfd);
-    if (!server_cert) {
-        BLog(BLOG_ERROR, "SSL_PeerCertificate failed");
-        PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
-        goto fail1;
-    }
-    
-    if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), server_cert, PR_TRUE, certUsageSSLServer, SSL_RevealPinArg(pio->connect.sock.ssl_prfd)) != SECSuccess) {
-        goto fail2;
-    }
-    
-    // compare to certificate provided by the server
-    if (!compare_certificate(pio, server_cert)) {
-        PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
-        goto fail2;
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
+        BLog(BLOG_NOTICE, "connection closed");
+    } else {
+        BLog(BLOG_NOTICE, "connection error");
     }
     
-    ret = SECSuccess;
-    
-fail2:
-    CERT_DestroyCertificate(server_cert);
-fail1:
-    return ret;
+    reset_and_report_error(pio);
+    return;
 }
 
-SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey)
+void connect_sslcon_handler (StreamPeerIO *pio, int event)
 {
+    DebugObject_Access(&pio->d_obj);
     ASSERT(pio->ssl)
     ASSERT(pio->mode == MODE_CONNECT)
-    ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
-    DebugObject_Access(&pio->d_obj);
+    ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING)
+    ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR)
     
-    CERTCertificate *cert = CERT_DupCertificate(pio->connect.ssl_cert);
-    if (!cert) {
-        BLog(BLOG_ERROR, "CERT_DupCertificate failed");
-        goto fail0;
-    }
-    
-    SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(pio->connect.ssl_key);
-    if (!key) {
-        BLog(BLOG_ERROR, "SECKEY_CopyPrivateKey failed");
-        goto fail1;
+    if (event == BSSLCONNECTION_EVENT_ERROR) {
+        BLog(BLOG_NOTICE, "SSL error");
+        
+        reset_and_report_error(pio);
+        return;
     }
     
-    *pRetCert = cert;
-    *pRetKey = key;
-    return SECSuccess;
-    
-fail1:
-    CERT_DestroyCertificate(cert);
-fail0:
-    return SECFailure;
-}
-
-void connecting_try_handshake (StreamPeerIO *pio)
-{
-    ASSERT(pio->ssl)
-    ASSERT(pio->mode == MODE_CONNECT)
+    // handshake complete
     ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
     
-    if (SSL_ForceHandshake(pio->connect.sock.ssl_prfd) != SECSuccess) {
-        PRErrorCode error = PR_GetError();
-        if (error == PR_WOULD_BLOCK_ERROR) {
-            BPRFileDesc_EnableEvent(&pio->connect.sock.ssl_bprfd, PR_POLL_READ);
-            return;
-        }
-        BLog(BLOG_NOTICE, "SSL_ForceHandshake failed (%d)", (int)error);
-        goto fail0;
-    }
-    
     // remove client certificate callback
     if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) {
         BLog(BLOG_ERROR, "SSL_GetClientAuthDataHook failed");
@@ -243,47 +204,35 @@ void connecting_try_handshake (StreamPeerIO *pio)
         goto fail0;
     }
     
-    // remove read handler
-    BPRFileDesc_RemoveEventHandler(&pio->connect.sock.ssl_bprfd, PR_POLL_READ);
-    
     // init password sender
-    PasswordSender_Init(&pio->connect.pwsender, pio->connect.password, 1, NULL, &pio->connect.sock.ssl_bprfd, (PasswordSender_handler)connecting_pwsender_handler, pio, pio->reactor);
+    SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BSSLConnection_GetSendIf(&pio->connect.sslcon), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler);
     
     // change state
     pio->connect.state = CONNECT_STATE_SENDING;
     
     return;
     
-    // cleanup
 fail0:
     reset_and_report_error(pio);
     return;
 }
 
-void connecting_handshake_read_handler (StreamPeerIO *pio, PRInt16 event)
+void pwsender_handler (StreamPeerIO *pio)
 {
-    ASSERT(pio->ssl)
-    ASSERT(pio->mode == MODE_CONNECT)
-    ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
     DebugObject_Access(&pio->d_obj);
-    
-    connecting_try_handshake(pio);
-    return;
-}
-
-static void connecting_pwsender_handler (StreamPeerIO *pio, int is_error)
-{
     ASSERT(pio->mode == MODE_CONNECT)
     ASSERT(pio->connect.state == CONNECT_STATE_SENDING)
-    DebugObject_Access(&pio->d_obj);
-    
-    if (is_error) {
-        BLog(BLOG_NOTICE, "error sending password");
-        goto fail0;
-    }
     
     // free password sender
-    PasswordSender_Free(&pio->connect.pwsender);
+    SingleStreamSender_Free(&pio->connect.pwsender);
+    
+    if (pio->ssl) {
+        // free BSSLConnection (we used the send interface)
+        BSSLConnection_Free(&pio->connect.sslcon);
+    } else {
+        // init connection send interface
+        BConnection_SendAsync_Free(&pio->connect.sock.con);
+    }
     
     // change state
     pio->connect.state = CONNECT_STATE_SENT;
@@ -303,25 +252,18 @@ fail0:
     return;
 }
 
-void error_handler (StreamPeerIO *pio, int component, int code)
-{
-    ASSERT(pio->sock)
-    DebugObject_Access(&pio->d_obj);
-    
-    // cleanup
-    reset_and_report_error(pio);
-    return;
-}
-
 void listener_handler_client (StreamPeerIO *pio, sslsocket *sock)
 {
+    DebugObject_Access(&pio->d_obj);
     ASSERT(pio->mode == MODE_LISTEN)
     ASSERT(pio->listen.state == LISTEN_STATE_LISTENER)
-    DebugObject_Access(&pio->d_obj);
     
     // remember socket
     pio->listen.sock = sock;
     
+    // set connection handler
+    BConnection_SetHandlers(&pio->listen.sock->con, pio, (BConnection_handler)connection_handler);
+    
     // change state
     pio->listen.state = LISTEN_STATE_GOTCLIENT;
     
@@ -352,101 +294,37 @@ void listener_handler_client (StreamPeerIO *pio, sslsocket *sock)
     
     return;
     
-    // cleanup
 fail0:
     reset_and_report_error(pio);
     return;
 }
 
-int init_persistent_io (StreamPeerIO *pio, PacketPassInterface *user_recv_if)
-{
-    // init error domain
-    FlowErrorDomain_Init(&pio->ioerrdomain, (FlowErrorDomain_handler)error_handler, pio);
-    
-    // init receiveing objects
-    StreamRecvConnector_Init(&pio->input_connector, BReactor_PendingGroup(pio->reactor));
-    if (!PacketProtoDecoder_Init(
-        &pio->input_decoder, FlowErrorReporter_Create(&pio->ioerrdomain, COMPONENT_DECODER),
-        StreamRecvConnector_GetOutput(&pio->input_connector), user_recv_if, BReactor_PendingGroup(pio->reactor)
-    )) {
-        goto fail1;
-    }
-    
-    // init sending objects
-    PacketCopier_Init(&pio->output_user_copier, pio->payload_mtu, BReactor_PendingGroup(pio->reactor));
-    PacketProtoEncoder_Init(&pio->output_user_ppe, PacketCopier_GetOutput(&pio->output_user_copier), BReactor_PendingGroup(pio->reactor));
-    PacketPassConnector_Init(&pio->output_connector, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor));
-    if (!SinglePacketBuffer_Init(&pio->output_user_spb, PacketProtoEncoder_GetOutput(&pio->output_user_ppe), PacketPassConnector_GetInput(&pio->output_connector), BReactor_PendingGroup(pio->reactor))) {
-        goto fail2;
-    }
-    
-    return 1;
-    
-fail2:
-    PacketPassConnector_Free(&pio->output_connector);
-    PacketProtoEncoder_Free(&pio->output_user_ppe);
-    PacketCopier_Free(&pio->output_user_copier);
-    PacketProtoDecoder_Free(&pio->input_decoder);
-fail1:
-    StreamRecvConnector_Free(&pio->input_connector);
-    return 0;
-}
-
-void free_persistent_io (StreamPeerIO *pio)
-{
-    // free sending objects
-    SinglePacketBuffer_Free(&pio->output_user_spb);
-    PacketPassConnector_Free(&pio->output_connector);
-    PacketProtoEncoder_Free(&pio->output_user_ppe);
-    PacketCopier_Free(&pio->output_user_copier);
-    
-    // free receiveing objects
-    PacketProtoDecoder_Free(&pio->input_decoder);
-    StreamRecvConnector_Free(&pio->input_connector);
-}
-
 int init_io (StreamPeerIO *pio, sslsocket *sock)
 {
     ASSERT(!pio->sock)
     
     // limit socket send buffer, else our scheduling is pointless
-    if (BSocket_SetSendBuffer(&sock->sock, STREAMPEERIO_SOCKET_SEND_BUFFER) < 0) {
-        BLog(BLOG_WARNING, "BSocket_SetSendBuffer failed");
+    if (!BConnection_SetSendBuffer(&sock->con, STREAMPEERIO_SOCKET_SEND_BUFFER)) {
+        BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed");
     }
     
-    // init receiving
-    StreamRecvInterface *source_interface;
     if (pio->ssl) {
-        PRStreamSource_Init(
-            &pio->input_source.ssl, FlowErrorReporter_Create(&pio->ioerrdomain, COMPONENT_SOURCE),
-            &sock->ssl_bprfd, BReactor_PendingGroup(pio->reactor)
-        );
-        source_interface = PRStreamSource_GetOutput(&pio->input_source.ssl);
+        // init BSSLConnection
+        BSSLConnection_Init(&pio->sslcon, sock->ssl_prfd, 0, pio->reactor, pio, (BSSLConnection_handler)sslcon_handler);
     } else {
-        StreamSocketSource_Init(
-            &pio->input_source.plain, FlowErrorReporter_Create(&pio->ioerrdomain, COMPONENT_SOURCE),
-            &sock->sock, BReactor_PendingGroup(pio->reactor)
-        );
-        source_interface = StreamSocketSource_GetOutput(&pio->input_source.plain);
+        // init connection interfaces
+        BConnection_SendAsync_Init(&sock->con);
+        BConnection_RecvAsync_Init(&sock->con);
     }
-    StreamRecvConnector_ConnectInput(&pio->input_connector, source_interface);
+    
+    StreamPassInterface *send_if = (pio->ssl ? BSSLConnection_GetSendIf(&pio->sslcon) : BConnection_SendAsync_GetIf(&sock->con));
+    StreamRecvInterface *recv_if = (pio->ssl ? BSSLConnection_GetRecvIf(&pio->sslcon) : BConnection_RecvAsync_GetIf(&sock->con));
+    
+    // init receiving
+    StreamRecvConnector_ConnectInput(&pio->input_connector, recv_if);
     
     // init sending
-    StreamPassInterface *sink_interface;
-    if (pio->ssl) {
-        PRStreamSink_Init(
-            &pio->output_sink.ssl, FlowErrorReporter_Create(&pio->ioerrdomain, COMPONENT_SINK),
-            &sock->ssl_bprfd, BReactor_PendingGroup(pio->reactor)
-        );
-        sink_interface = PRStreamSink_GetInput(&pio->output_sink.ssl);
-    } else {
-        StreamSocketSink_Init(
-            &pio->output_sink.plain, FlowErrorReporter_Create(&pio->ioerrdomain, COMPONENT_SINK),
-            &sock->sock, BReactor_PendingGroup(pio->reactor)
-        );
-        sink_interface = StreamSocketSink_GetInput(&pio->output_sink.plain);
-    }
-    PacketStreamSender_Init(&pio->output_pss, sink_interface, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor));
+    PacketStreamSender_Init(&pio->output_pss, send_if, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor));
     PacketPassConnector_ConnectOutput(&pio->output_connector, PacketStreamSender_GetInput(&pio->output_pss));
     
     pio->sock = sock;
@@ -464,23 +342,104 @@ void free_io (StreamPeerIO *pio)
     // free sending
     PacketPassConnector_DisconnectOutput(&pio->output_connector);
     PacketStreamSender_Free(&pio->output_pss);
-    if (pio->ssl) {
-        PRStreamSink_Free(&pio->output_sink.ssl);
-    } else {
-        StreamSocketSink_Free(&pio->output_sink.plain);
-    }
     
     // free receiving
     StreamRecvConnector_DisconnectInput(&pio->input_connector);
+    
     if (pio->ssl) {
-        PRStreamSource_Free(&pio->input_source.ssl);
+        // free BSSLConnection
+        BSSLConnection_Free(&pio->sslcon);
     } else {
-        StreamSocketSource_Free(&pio->input_source.plain);
+        // free connection interfaces
+        BConnection_RecvAsync_Free(&pio->sock->con);
+        BConnection_SendAsync_Free(&pio->sock->con);
     }
     
     pio->sock = NULL;
 }
 
+void sslcon_handler (StreamPeerIO *pio, int event)
+{
+    DebugObject_Access(&pio->d_obj);
+    ASSERT(pio->ssl)
+    ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN)
+    ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state == CONNECT_STATE_FINISHED)
+    ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state == LISTEN_STATE_FINISHED)
+    ASSERT(event == BSSLCONNECTION_EVENT_ERROR)
+    
+    BLog(BLOG_NOTICE, "SSL error");
+    
+    reset_and_report_error(pio);
+    return;
+}
+
+SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
+{
+    ASSERT(pio->ssl)
+    ASSERT(pio->mode == MODE_CONNECT)
+    ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
+    DebugObject_Access(&pio->d_obj);
+    
+    // This callback is used to bypass checking the server's domain name, as peers
+    // don't have domain names. We byte-compare the certificate to the one reported
+    // by the server anyway.
+    
+    SECStatus ret = SECFailure;
+    
+    CERTCertificate *server_cert = SSL_PeerCertificate(pio->connect.sock.ssl_prfd);
+    if (!server_cert) {
+        BLog(BLOG_ERROR, "SSL_PeerCertificate failed");
+        PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
+        goto fail1;
+    }
+    
+    if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), server_cert, PR_TRUE, certUsageSSLServer, SSL_RevealPinArg(pio->connect.sock.ssl_prfd)) != SECSuccess) {
+        goto fail2;
+    }
+    
+    // compare to certificate provided by the server
+    if (!compare_certificate(pio, server_cert)) {
+        PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
+        goto fail2;
+    }
+    
+    ret = SECSuccess;
+    
+fail2:
+    CERT_DestroyCertificate(server_cert);
+fail1:
+    return ret;
+}
+
+SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey)
+{
+    ASSERT(pio->ssl)
+    ASSERT(pio->mode == MODE_CONNECT)
+    ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
+    DebugObject_Access(&pio->d_obj);
+    
+    CERTCertificate *cert = CERT_DupCertificate(pio->connect.ssl_cert);
+    if (!cert) {
+        BLog(BLOG_ERROR, "CERT_DupCertificate failed");
+        goto fail0;
+    }
+    
+    SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(pio->connect.ssl_key);
+    if (!key) {
+        BLog(BLOG_ERROR, "SECKEY_CopyPrivateKey failed");
+        goto fail1;
+    }
+    
+    *pRetCert = cert;
+    *pRetKey = key;
+    return SECSuccess;
+    
+fail1:
+    CERT_DestroyCertificate(cert);
+fail0:
+    return SECFailure;
+}
+
 int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert)
 {
     ASSERT(pio->ssl)
@@ -505,16 +464,21 @@ void reset_state (StreamPeerIO *pio)
                 case LISTEN_STATE_FINISHED:
                     free_io(pio);
                 case LISTEN_STATE_GOTCLIENT:
-                    cleanup_socket(pio->listen.sock, pio->ssl);
+                    if (pio->ssl) {
+                        ASSERT_FORCE(PR_Close(pio->listen.sock->ssl_prfd) == PR_SUCCESS)
+                        BConnection_RecvAsync_Free(&pio->listen.sock->con);
+                        BConnection_SendAsync_Free(&pio->listen.sock->con);
+                    }
+                    BConnection_Free(&pio->listen.sock->con);
                     free(pio->listen.sock);
-                    break;
                 case LISTEN_STATE_LISTENER:
-                    PasswordListener_RemoveEntry(pio->listen.listener, &pio->listen.pwentry);
+                    if (pio->listen.state == LISTEN_STATE_LISTENER) {
+                        PasswordListener_RemoveEntry(pio->listen.listener, &pio->listen.pwentry);
+                    }
                     break;
                 default:
                     ASSERT(0);
             }
-            pio->mode = MODE_NONE;
             break;
         case MODE_CONNECT:
             switch (pio->connect.state) {
@@ -523,39 +487,36 @@ void reset_state (StreamPeerIO *pio)
                 case CONNECT_STATE_SENT:
                 case CONNECT_STATE_SENDING:
                     if (pio->connect.state == CONNECT_STATE_SENDING) {
-                        PasswordSender_Free(&pio->connect.pwsender);
+                        SingleStreamSender_Free(&pio->connect.pwsender);
+                        if (!pio->ssl) {
+                            BConnection_SendAsync_Free(&pio->connect.sock.con);
+                        }
                     }
                 case CONNECT_STATE_HANDSHAKE:
                     if (pio->ssl) {
-                        BPRFileDesc_Free(&pio->connect.sock.ssl_bprfd);
+                        if (pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING) {
+                            BSSLConnection_Free(&pio->connect.sslcon);
+                        }
                         ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS)
+                        BConnection_RecvAsync_Free(&pio->connect.sock.con);
+                        BConnection_SendAsync_Free(&pio->connect.sock.con);
                     }
+                    BConnection_Free(&pio->connect.sock.con);
                 case CONNECT_STATE_CONNECTING:
-                    BSocket_Free(&pio->connect.sock.sock);
+                    BConnector_Free(&pio->connect.connector);
                     break;
                 default:
                     ASSERT(0);
             }
-            pio->mode = MODE_NONE;
             break;
         default:
             ASSERT(0);
     }
     
-    ASSERT(!pio->sock)
-}
-
-void cleanup_socket (sslsocket *sock, int ssl)
-{
-    if (ssl) {
-        // free BPRFileDesc
-        BPRFileDesc_Free(&sock->ssl_bprfd);
-        // free SSL NSPR file descriptor
-        ASSERT_FORCE(PR_Close(sock->ssl_prfd) == PR_SUCCESS)
-    }
+    // set mode none
+    pio->mode = MODE_NONE;
     
-    // free socket
-    BSocket_Free(&sock->sock);
+    ASSERT(!pio->sock)
 }
 
 void reset_and_report_error (StreamPeerIO *pio)
@@ -581,6 +542,7 @@ int StreamPeerIO_Init (
     ASSERT(ssl == 0 || ssl == 1)
     ASSERT(payload_mtu >= 0)
     ASSERT(PacketPassInterface_GetMTU(user_recv_if) >= payload_mtu)
+    ASSERT(handler_error)
     
     // init arguments
     pio->reactor = reactor;
@@ -599,9 +561,21 @@ int StreamPeerIO_Init (
         goto fail0;
     }
     
-    // init persistent I/O modules
-    if (!init_persistent_io(pio, user_recv_if)) {
-        goto fail0;
+    // init receiveing objects
+    StreamRecvConnector_Init(&pio->input_connector, BReactor_PendingGroup(pio->reactor));
+    FlowErrorDomain_Init(&pio->input_decoder_domain, (FlowErrorDomain_handler)decoder_handler, pio);
+    if (!PacketProtoDecoder_Init(&pio->input_decoder, FlowErrorReporter_Create(&pio->input_decoder_domain, 0), StreamRecvConnector_GetOutput(&pio->input_connector), user_recv_if, BReactor_PendingGroup(pio->reactor))) {
+        BLog(BLOG_ERROR, "FlowErrorDomain_Init failed");
+        goto fail1;
+    }
+    
+    // init sending objects
+    PacketCopier_Init(&pio->output_user_copier, pio->payload_mtu, BReactor_PendingGroup(pio->reactor));
+    PacketProtoEncoder_Init(&pio->output_user_ppe, PacketCopier_GetOutput(&pio->output_user_copier), BReactor_PendingGroup(pio->reactor));
+    PacketPassConnector_Init(&pio->output_connector, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor));
+    if (!SinglePacketBuffer_Init(&pio->output_user_spb, PacketProtoEncoder_GetOutput(&pio->output_user_ppe), PacketPassConnector_GetInput(&pio->output_connector), BReactor_PendingGroup(pio->reactor))) {
+        BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
+        goto fail2;
     }
     
     // set mode none
@@ -611,9 +585,15 @@ int StreamPeerIO_Init (
     pio->sock = NULL;
     
     DebugObject_Init(&pio->d_obj);
-    
     return 1;
     
+fail2:
+    PacketPassConnector_Free(&pio->output_connector);
+    PacketProtoEncoder_Free(&pio->output_user_ppe);
+    PacketCopier_Free(&pio->output_user_copier);
+    PacketProtoDecoder_Free(&pio->input_decoder);
+fail1:
+    StreamRecvConnector_Free(&pio->input_connector);
 fail0:
     return 0;
 }
@@ -625,8 +605,15 @@ void StreamPeerIO_Free (StreamPeerIO *pio)
     // reset state
     reset_state(pio);
     
-    // free persistent I/O modules
-    free_persistent_io(pio);
+    // free sending objects
+    SinglePacketBuffer_Free(&pio->output_user_spb);
+    PacketPassConnector_Free(&pio->output_connector);
+    PacketProtoEncoder_Free(&pio->output_user_ppe);
+    PacketCopier_Free(&pio->output_user_copier);
+    
+    // free receiveing objects
+    PacketProtoDecoder_Free(&pio->input_decoder);
+    StreamRecvConnector_Free(&pio->input_connector);
 }
 
 PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio)
@@ -638,28 +625,18 @@ PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio)
 
 int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key)
 {
-    ASSERT(!BAddr_IsInvalid(&addr))
     DebugObject_Access(&pio->d_obj);
+    ASSERT(BConnection_AddressSupported(addr))
     
     // reset state
     reset_state(pio);
     
-    // create socket
-    if (BSocket_Init(&pio->connect.sock.sock, pio->reactor, addr.type, BSOCKET_TYPE_STREAM) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Init failed");
+    // init connector
+    if (!BConnector_Init(&pio->connect.connector, addr, pio->reactor, pio, (BConnector_handler)connector_handler)) {
+        BLog(BLOG_ERROR, "BConnector_Init failed");
         goto fail0;
     }
     
-    // attempt connection
-    if (BSocket_Connect(&pio->connect.sock.sock, &addr, 1) >= 0 || BSocket_GetError(&pio->connect.sock.sock) != BSOCKET_ERROR_IN_PROGRESS) {
-        BLog(BLOG_NOTICE, "BSocket_Connect failed");
-        goto fail1;
-    }
-    
-    // waiting for connection result
-    BSocket_AddEventHandler(&pio->connect.sock.sock, BSOCKET_CONNECT, (BSocket_handler)connecting_connect_handler, pio);
-    BSocket_EnableEvent(&pio->connect.sock.sock, BSOCKET_CONNECT);
-    
     // remember data
     if (pio->ssl) {
         pio->connect.ssl_cert = ssl_cert;
@@ -673,16 +650,14 @@ int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERT
     
     return 1;
     
-fail1:
-    BSocket_Free(&pio->connect.sock.sock);
 fail0:
     return 0;
 }
 
 void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password)
 {
-    ASSERT(listener->ssl == pio->ssl)
     DebugObject_Access(&pio->d_obj);
+    ASSERT(listener->ssl == pio->ssl)
     
     // reset state
     reset_state(pio);

+ 10 - 21
client/StreamPeerIO.h

@@ -35,7 +35,7 @@
 #include <misc/debug.h>
 #include <base/DebugObject.h>
 #include <system/BReactor.h>
-#include <system/BSocket.h>
+#include <system/BConnection.h>
 #include <structure/LinkedList2.h>
 #include <flow/PacketProtoDecoder.h>
 #include <flow/PacketStreamSender.h>
@@ -44,12 +44,8 @@
 #include <flow/PacketCopier.h>
 #include <flow/PacketPassConnector.h>
 #include <flow/StreamRecvConnector.h>
-#include <flowextra/StreamSocketSource.h>
-#include <flowextra/StreamSocketSink.h>
-#include <nspr_support/PRStreamSink.h>
-#include <nspr_support/PRStreamSource.h>
+#include <flow/SingleStreamSender.h>
 #include <client/PasswordListener.h>
-#include <client/PasswordSender.h>
 
 #define STREAMPEERIO_SOCKET_SEND_BUFFER 4096
 
@@ -81,9 +77,6 @@ typedef struct {
     
     // persistent I/O modules
     
-    // I/O error domain
-    FlowErrorDomain ioerrdomain;
-    
     // base sending objects
     PacketCopier output_user_copier;
     PacketProtoEncoder output_user_ppe;
@@ -92,6 +85,7 @@ typedef struct {
     
     // receiving objects
     StreamRecvConnector input_connector;
+    FlowErrorDomain input_decoder_domain;
     PacketProtoDecoder input_decoder;
     
     // connection side
@@ -110,27 +104,20 @@ typedef struct {
             int state;
             CERTCertificate *ssl_cert;
             SECKEYPrivateKey *ssl_key;
+            BConnector connector;
             sslsocket sock;
+            BSSLConnection sslcon;
             uint64_t password;
-            PasswordSender pwsender;
+            SingleStreamSender pwsender;
         } connect;
     };
     
     // socket data
     sslsocket *sock;
+    BSSLConnection sslcon;
     
     // sending objects
     PacketStreamSender output_pss;
-    union {
-        StreamSocketSink plain;
-        PRStreamSink ssl;
-    } output_sink;
-    
-    // receiving objects
-    union {
-        StreamSocketSource plain;
-        PRStreamSource ssl;
-    } input_source;
     
     DebugObject d_obj;
 } StreamPeerIO;
@@ -139,6 +126,8 @@ typedef struct {
  * Initializes the object.
  * The object is initialized in default state.
  * {@link BLog_Init} must have been done.
+ * {@link BNetwork_GlobalInit} must have been done.
+ * {@link BSSLConnection_GlobalInit} must have been done if using SSL.
  *
  * @param pio the object
  * @param reactor reactor we live in
@@ -187,7 +176,7 @@ PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio);
  * On failure, the object enters default state.
  *
  * @param pio the object
- * @param addr address to connect to. Must be recognized and not invalid.
+ * @param addr address to connect to. Must be supported according to {@link BConnection_AddressSupported}.
  * @param password identification code to send to the peer
  * @param ssl_cert if using SSL, the client certificate to use. This object does not
  *                 take ownership of the certificate; it must remain valid until

+ 27 - 25
client/client.c

@@ -20,18 +20,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-/*
- NOTE:
- This program works with I/O inside the BPending job environment.
- A consequence of this is that in response to an input, we can't
- directly do any output, but instead have to schedule outputs.
- Because all the buffers used (e.g. server send buffer, data buffers in DataProto)
- are based on flow components, it is impossible to directly write two or more
- packets to a buffer.
- To, for instance, send two packets to a buffer, we have to first schedule
- writing the second packet (using BPending), then send the first one.
-*/
-
 #include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
@@ -49,14 +37,15 @@
 #include <misc/loglevel.h>
 #include <misc/loggers_string.h>
 #include <structure/LinkedList2.h>
+#include <base/DebugObject.h>
+#include <base/BLog.h>
 #include <security/BSecurity.h>
 #include <security/BRandom.h>
-#include <nspr_support/DummyPRFileDesc.h>
-#include <nspr_support/BSocketPRFileDesc.h>
-#include <base/BLog.h>
 #include <system/BSignal.h>
 #include <system/BTime.h>
-#include <base/DebugObject.h>
+#include <system/BNetwork.h>
+#include <nspr_support/DummyPRFileDesc.h>
+#include <nspr_support/BSSLConnection.h>
 #include <server_connection/ServerConnection.h>
 #include <tuntap/BTap.h>
 
@@ -278,6 +267,9 @@ static void peer_udp_pio_handler_seed_warning (struct peer_data *peer);
 // handler from DatagramPeerIO when a new OTP seed can be recognized once it was provided to it
 static void peer_udp_pio_handler_seed_ready (struct peer_data *peer);
 
+// handler from DatagramPeerIO when an error occurs on the connection
+static void peer_udp_pio_handler_error (struct peer_data *peer);
+
 // handler from StreamPeerIO when an error occurs on the connection
 static void peer_tcp_pio_handler_error (struct peer_data *peer);
 
@@ -385,9 +377,9 @@ int main (int argc, char *argv[])
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
-    // initialize sockets
-    if (BSocket_GlobalInit() < 0) {
-        BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
+    // initialize network
+    if (!BNetwork_GlobalInit()) {
+        BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
         goto fail1;
     }
     
@@ -435,8 +427,8 @@ int main (int argc, char *argv[])
             BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed");
             goto fail3;
         }
-        if (!BSocketPRFileDesc_GlobalInit()) {
-            BLog(BLOG_ERROR, "BSocketPRFileDesc_GlobalInit failed");
+        if (!BSSLConnection_GlobalInit()) {
+            BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed");
             goto fail3;
         }
         
@@ -1469,11 +1461,10 @@ int peer_init_link (struct peer_data *peer)
         }
         
         // set handlers
-        DatagramPeerIO_SetHandlers(
-            &peer->pio.udp.pio,
+        DatagramPeerIO_SetHandlers(&peer->pio.udp.pio, peer,
+            (DatagramPeerIO_handler_error)peer_udp_pio_handler_error,
             (DatagramPeerIO_handler_otp_warning)peer_udp_pio_handler_seed_warning,
-            (DatagramPeerIO_handler_otp_ready)peer_udp_pio_handler_seed_ready,
-            peer
+            (DatagramPeerIO_handler_otp_ready)peer_udp_pio_handler_seed_ready
         );
         
         // init send seed state
@@ -2050,6 +2041,17 @@ void peer_udp_pio_handler_seed_ready (struct peer_data *peer)
     peer_send_confirmseed(peer, peer->pio.udp.pending_recvseed_id);
 }
 
+void peer_udp_pio_handler_error (struct peer_data *peer)
+{
+    ASSERT(options.transport_mode == TRANSPORT_MODE_UDP)
+    ASSERT(peer->have_link)
+    
+    peer_log(peer, BLOG_NOTICE, "UDP connection failed");
+    
+    peer_reset(peer);
+    return;
+}
+
 void peer_tcp_pio_handler_error (struct peer_data *peer)
 {
     ASSERT(options.transport_mode == TRANSPORT_MODE_TCP)

+ 34 - 29
dhcpclient/BDHCPClient.c

@@ -57,11 +57,15 @@ static const struct sock_filter dhcp_sock_filter[] = {
     BPF_STMT(BPF_RET + BPF_K, 0)                                  // ignore
 };
 
-static void error_handler (BDHCPClient *o, int component, int code)
+static void dgram_handler (BDHCPClient *o, int event)
 {
     DebugObject_Access(&o->d_obj);
     
-    BLog(BLOG_ERROR, "error");
+    BLog(BLOG_ERROR, "packet socket error");
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BDHCPCLIENT_EVENT_ERROR));
+    return;
 }
 
 static void dhcp_handler (BDHCPClient *o, int event)
@@ -163,9 +167,9 @@ int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDH
     
     int dhcp_mtu = if_mtu - IPUDP_OVERHEAD;
     
-    // init socket
-    if (BSocket_Init(&o->sock, o->reactor, BADDR_TYPE_PACKET, BSOCKET_TYPE_DGRAM) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Init failed");
+    // init dgram
+    if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) {
+        BLog(BLOG_ERROR, "BDatagram_Init failed");
         goto fail0;
     }
     
@@ -178,31 +182,32 @@ int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDH
             .len = flen,
             .filter = filter
         };
-        if (setsockopt(BSocket_SockFd(&o->sock), SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) {
+        if (setsockopt(BDatagram_GetFd(&o->dgram), SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) {
             BLog(BLOG_NOTICE, "not using socket filter");
         }
     }
     
-    // bind socket
+    // bind dgram
     BAddr bind_addr;
     BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac);
-    if (BSocket_Bind(&o->sock, &bind_addr) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Bind failed");
+    if (!BDatagram_Bind(&o->dgram, bind_addr)) {
+        BLog(BLOG_ERROR, "BDatagram_Bind failed");
         goto fail1;
     }
     
-    // init error handler
-    FlowErrorDomain_Init(&o->domain, (FlowErrorDomain_handler)error_handler, o);
-    
-    // init sending
-    
-    // init sink
+    // set dgram send addresses
     BAddr dest_addr;
     uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
     BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac);
     BIPAddr local_addr;
     BIPAddr_InitInvalid(&local_addr);
-    DatagramSocketSink_Init(&o->send_sink, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), &o->sock, if_mtu, dest_addr, local_addr, BReactor_PendingGroup(o->reactor));
+    BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr);
+
+    // init dgram interfaces
+    BDatagram_SendAsync_Init(&o->dgram, if_mtu);
+    BDatagram_RecvAsync_Init(&o->dgram, if_mtu);
+    
+    // init sending
     
     // init copier
     PacketCopier_Init(&o->send_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor));
@@ -211,16 +216,13 @@ int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDH
     DHCPIpUdpEncoder_Init(&o->send_encoder, PacketCopier_GetOutput(&o->send_copier), BReactor_PendingGroup(o->reactor));
     
     // init buffer
-    if (!SinglePacketBuffer_Init(&o->send_buffer, DHCPIpUdpEncoder_GetOutput(&o->send_encoder), DatagramSocketSink_GetInput(&o->send_sink), BReactor_PendingGroup(o->reactor))) {
+    if (!SinglePacketBuffer_Init(&o->send_buffer, DHCPIpUdpEncoder_GetOutput(&o->send_encoder), BDatagram_SendAsync_GetIf(&o->dgram), BReactor_PendingGroup(o->reactor))) {
         BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
         goto fail2;
     }
     
     // init receiving
     
-    // init source
-    DatagramSocketSource_Init(&o->recv_source, FlowErrorReporter_Create(&o->domain, COMPONENT_SOURCE), &o->sock, if_mtu, BReactor_PendingGroup(o->reactor));
-    
     // init copier
     PacketCopier_Init(&o->recv_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor));
     
@@ -228,7 +230,7 @@ int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDH
     DHCPIpUdpDecoder_Init(&o->recv_decoder, PacketCopier_GetInput(&o->recv_copier), BReactor_PendingGroup(o->reactor));
     
     // init buffer
-    if (!SinglePacketBuffer_Init(&o->recv_buffer, DatagramSocketSource_GetOutput(&o->recv_source), DHCPIpUdpDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) {
+    if (!SinglePacketBuffer_Init(&o->recv_buffer, BDatagram_RecvAsync_GetIf(&o->dgram), DHCPIpUdpDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) {
         BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
         goto fail3;
     }
@@ -242,8 +244,8 @@ int BDHCPClient_Init (BDHCPClient *o, const char *ifname, BReactor *reactor, BDH
     // set not up
     o->up = 0;
     
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
     DebugObject_Init(&o->d_obj);
-    
     return 1;
     
 fail4:
@@ -251,14 +253,14 @@ fail4:
 fail3:
     DHCPIpUdpDecoder_Free(&o->recv_decoder);
     PacketCopier_Free(&o->recv_copier);
-    DatagramSocketSource_Free(&o->recv_source);
     SinglePacketBuffer_Free(&o->send_buffer);
 fail2:
     DHCPIpUdpEncoder_Free(&o->send_encoder);
     PacketCopier_Free(&o->send_copier);
-    DatagramSocketSink_Free(&o->send_sink);
+    BDatagram_RecvAsync_Free(&o->dgram);
+    BDatagram_SendAsync_Free(&o->dgram);
 fail1:
-    BSocket_Free(&o->sock);
+    BDatagram_Free(&o->dgram);
 fail0:
     return 0;
 }
@@ -266,6 +268,7 @@ fail0:
 void BDHCPClient_Free (BDHCPClient *o)
 {
     DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
     
     // free dhcp
     BDHCPClientCore_Free(&o->dhcp);
@@ -274,16 +277,18 @@ void BDHCPClient_Free (BDHCPClient *o)
     SinglePacketBuffer_Free(&o->recv_buffer);
     DHCPIpUdpDecoder_Free(&o->recv_decoder);
     PacketCopier_Free(&o->recv_copier);
-    DatagramSocketSource_Free(&o->recv_source);
     
     // free sending
     SinglePacketBuffer_Free(&o->send_buffer);
     DHCPIpUdpEncoder_Free(&o->send_encoder);
     PacketCopier_Free(&o->send_copier);
-    DatagramSocketSink_Free(&o->send_sink);
     
-    // free socket
-    BSocket_Free(&o->sock);
+    // free dgram interfaces
+    BDatagram_RecvAsync_Free(&o->dgram);
+    BDatagram_SendAsync_Free(&o->dgram);
+    
+    // free dgram
+    BDatagram_Free(&o->dgram);
 }
 
 int BDHCPClient_IsUp (BDHCPClient *o)

+ 4 - 7
dhcpclient/BDHCPClient.h

@@ -28,17 +28,16 @@
 #define BADVPN_DHCPCLIENT_BDHCPCLIENT_H
 
 #include <base/DebugObject.h>
-#include <system/BSocket.h>
+#include <system/BDatagram.h>
 #include <flow/PacketCopier.h>
 #include <flow/SinglePacketBuffer.h>
 #include <dhcpclient/BDHCPClientCore.h>
 #include <dhcpclient/DHCPIpUdpDecoder.h>
 #include <dhcpclient/DHCPIpUdpEncoder.h>
-#include <flowextra/DatagramSocketSink.h>
-#include <flowextra/DatagramSocketSource.h>
 
 #define BDHCPCLIENT_EVENT_UP 1
 #define BDHCPCLIENT_EVENT_DOWN 2
+#define BDHCPCLIENT_EVENT_ERROR 3
 
 #define BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS
 
@@ -46,20 +45,18 @@ typedef void (*BDHCPClient_handler) (void *user, int event);
 
 typedef struct {
     BReactor *reactor;
-    BSocket sock;
+    BDatagram dgram;
     BDHCPClient_handler handler;
     void *user;
-    FlowErrorDomain domain;
     PacketCopier send_copier;
     DHCPIpUdpEncoder send_encoder;
     SinglePacketBuffer send_buffer;
-    DatagramSocketSink send_sink;
-    DatagramSocketSource recv_source;
     SinglePacketBuffer recv_buffer;
     DHCPIpUdpDecoder recv_decoder;
     PacketCopier recv_copier;
     BDHCPClientCore dhcp;
     int up;
+    DebugError d_err;
     DebugObject d_obj;
 } BDHCPClient;
 

+ 13 - 0
examples/dhcpclient_test.c

@@ -28,6 +28,7 @@
 #include <system/BReactor.h>
 #include <system/BSignal.h>
 #include <system/BTime.h>
+#include <system/BNetwork.h>
 #include <dhcpclient/BDHCPClient.h>
 
 BReactor reactor;
@@ -53,6 +54,11 @@ int main (int argc, char **argv)
     
     BLog_InitStdout();
     
+    if (!BNetwork_GlobalInit()) {
+        DEBUG("BNetwork_GlobalInit failed");
+        goto fail1;
+    }
+    
     if (!BReactor_Init(&reactor)) {
         DEBUG("BReactor_Init failed");
         goto fail1;
@@ -123,6 +129,13 @@ void dhcp_handler (void *unused, int event)
             printf("DHCP: down\n");
         } break;
         
+        case BDHCPCLIENT_EVENT_ERROR: {
+            printf("DHCP: error\n");
+            
+            // exit reactor
+            BReactor_Quit(&reactor, 0);
+        } break;
+        
         default:
             ASSERT(0);
     }

+ 21 - 20
examples/stdin_input.c

@@ -27,19 +27,17 @@
 #include <stdio.h>
 #include <stddef.h>
 
+#include <base/DebugObject.h>
 #include <system/BReactor.c>
-#include <system/BSocket.h>
+#include <system/BNetwork.h>
+#include <system/BConnection.h>
 #include <system/BUnixSignal.h>
-#include <base/DebugObject.h>
-#include <flowextra/StreamSocketSource.h>
 
 #define BUF_SIZE 64
 
 BReactor reactor;
-BSocket pipe_bsock;
+BConnection pipe_con;
 BUnixSignal usignal;
-FlowErrorDomain errdomain;
-StreamSocketSource source;
 StreamRecvInterface *source_if;
 uint8_t buf[BUF_SIZE + 1];
 
@@ -51,16 +49,16 @@ static void signal_handler (void *user, int signo)
     BReactor_Quit(&reactor, 1);
 }
 
-static void source_error_handler (void *user, int component, int code)
+static void connection_handler (void *user, int event)
 {
-    if (code == 0) {
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
         fprintf(stderr, "pipe closed\n");
     } else {
         fprintf(stderr, "pipe error\n");
     }
     
     // exit event loop
-    BReactor_Quit(&reactor, (code == 0 ? 0 : 1));
+    BReactor_Quit(&reactor, (event == BCONNECTION_EVENT_RECVCLOSED ? 0 : 1));
 }
 
 static void input_handler_done (void *user, int data_len)
@@ -79,6 +77,12 @@ int main ()
     
     BLog_InitStdout();
     
+    // init network
+    if (!BNetwork_GlobalInit()) {
+        fprintf(stderr, "BNetwork_GlobalInit failed\n");
+        goto fail1;
+    }
+    
     // init reactor (event loop)
     if (!BReactor_Init(&reactor)) {
         fprintf(stderr, "BReactor_Init failed\n");
@@ -95,18 +99,15 @@ int main ()
         goto fail2;
     }
     
-    // init BSocket object backed by the stdin fd
-    if (BSocket_InitPipe(&pipe_bsock, &reactor, 0) < 0) {
-        fprintf(stderr, "BSocket_InitPipe failed\n");
+    // init BConnection object backed by the stdin fd
+    if (!BConnection_Init(&pipe_con, BCONNECTION_SOURCE_PIPE(0), &reactor, NULL, connection_handler)) {
+        fprintf(stderr, "BConnection_Init failed\n");
         goto fail3;
     }
     
-    // init error handler
-    FlowErrorDomain_Init(&errdomain, source_error_handler, NULL);
-    
-    // init source (object for reading from a stream BSocket using StreamRecvInterface)
-    StreamSocketSource_Init(&source, FlowErrorReporter_Create(&errdomain, 0), &pipe_bsock, BReactor_PendingGroup(&reactor));
-    source_if = StreamSocketSource_GetOutput(&source);
+    // init connection receive interface
+    BConnection_RecvAsync_Init(&pipe_con);
+    source_if = BConnection_RecvAsync_GetIf(&pipe_con);
     
     // init receive done callback
     StreamRecvInterface_Receiver_Init(source_if, input_handler_done, NULL);
@@ -117,8 +118,8 @@ int main ()
     // run event loop
     ret = BReactor_Exec(&reactor);
     
-    StreamSocketSource_Free(&source);
-    BSocket_Free(&pipe_bsock);
+    BConnection_RecvAsync_Free(&pipe_con);
+    BConnection_Free(&pipe_con);
 fail3:
     BUnixSignal_Free(&usignal, 0);
 fail2:

+ 8 - 12
flooder/flooder.c

@@ -35,10 +35,10 @@
 #include <base/BLog.h>
 #include <system/BReactor.h>
 #include <system/BSignal.h>
+#include <system/BNetwork.h>
 #include <flow/SinglePacketBuffer.h>
 #include <flow/PacketProtoEncoder.h>
-#include <nspr_support/DummyPRFileDesc.h>
-#include <nspr_support/BSocketPRFileDesc.h>
+#include <nspr_support/BSSLConnection.h>
 #include <server_connection/ServerConnection.h>
 
 #ifndef BADVPN_USE_WINAPI
@@ -202,9 +202,9 @@ int main (int argc, char *argv[])
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
-    // initialize sockets
-    if (BSocket_GlobalInit() < 0) {
-        BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
+    // initialize network
+    if (!BNetwork_GlobalInit()) {
+        BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
         goto fail1;
     }
     
@@ -234,13 +234,9 @@ int main (int argc, char *argv[])
         PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
         
         // register local NSPR file types
-        if (!DummyPRFileDesc_GlobalInit()) {
-            BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed");
-            goto fail2;
-        }
-        if (!BSocketPRFileDesc_GlobalInit()) {
-            BLog(BLOG_ERROR, "BSocketPRFileDesc_GlobalInit failed");
-            goto fail2;
+        if (!BSSLConnection_GlobalInit()) {
+            BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed");
+            goto fail3;
         }
         
         // init NSS

+ 2 - 0
flow/CMakeLists.txt

@@ -23,5 +23,7 @@ add_library(flow
     RouteBuffer.c
     PacketRouter.c
     LineBuffer.c
+    SingleStreamSender.c
+    SingleStreamReceiver.c
 )
 target_link_libraries(flow base)

+ 0 - 4
flowextra/CMakeLists.txt

@@ -1,8 +1,4 @@
 add_library(flowextra
-    DatagramSocketSource.c
-    DatagramSocketSink.c
-    StreamSocketSource.c
-    StreamSocketSink.c
     PacketPassInactivityMonitor.c
     KeepaliveIO.c
 )

+ 0 - 141
flowextra/DatagramSocketSink.c

@@ -1,141 +0,0 @@
-/**
- * @file DatagramSocketSink.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/debug.h>
-#include <base/BLog.h>
-
-#include "DatagramSocketSink.h"
-
-#include <generated/blog_channel_DatagramSocketSink.h>
-
-static void report_error (DatagramSocketSink *s, int error)
-{
-    FlowErrorReporter_ReportError(&s->rep, error);
-    return;
-}
-
-static void try_send (DatagramSocketSink *s)
-{
-    ASSERT(s->in_len >= 0)
-    
-    int res = BSocket_SendToFrom(s->bsock, s->in, s->in_len, &s->addr, &s->local_addr);
-    if (res < 0 && BSocket_GetError(s->bsock) == BSOCKET_ERROR_LATER) {
-        // wait for socket in socket_handler
-        BSocket_EnableEvent(s->bsock, BSOCKET_WRITE);
-        return;
-    }
-    
-    int old_len = s->in_len;
-    
-    // finish packet
-    s->in_len = -1;
-    PacketPassInterface_Done(&s->input);
-    
-    if (res < 0) {
-        BLog(BLOG_NOTICE, "BSocket_SendToFrom failed (%d)", BSocket_GetError(s->bsock));
-        report_error(s, DATAGRAMSOCKETSINK_ERROR_BSOCKET);
-        return;
-    }
-    else if (res != old_len) {
-        BLog(BLOG_NOTICE, "Sent only %d out of %d", res, old_len);
-        report_error(s, DATAGRAMSOCKETSINK_ERROR_WRONGSIZE);
-        return;
-    }
-}
-
-static void input_handler_send (DatagramSocketSink *s, uint8_t *data, int data_len)
-{
-    ASSERT(s->in_len == -1)
-    ASSERT(data_len >= 0)
-    DebugObject_Access(&s->d_obj);
-    
-    // set packet
-    s->in_len = data_len;
-    s->in = data;
-    
-    try_send(s);
-    return;
-}
-
-static void socket_handler (DatagramSocketSink *s, int event)
-{
-    ASSERT(s->in_len >= 0)
-    ASSERT(event == BSOCKET_WRITE)
-    DebugObject_Access(&s->d_obj);
-    
-    BSocket_DisableEvent(s->bsock, BSOCKET_WRITE);
-    
-    try_send(s);
-    return;
-}
-
-void DatagramSocketSink_Init (DatagramSocketSink *s, FlowErrorReporter rep, BSocket *bsock, int mtu, BAddr addr, BIPAddr local_addr, BPendingGroup *pg)
-{
-    ASSERT(mtu >= 0)
-    ASSERT(!BAddr_IsInvalid(&addr))
-    BIPAddr_Assert(&local_addr);
-    
-    // init arguments
-    s->rep = rep;
-    s->bsock = bsock;
-    s->addr = addr;
-    s->local_addr = local_addr;
-    
-    // add socket event handler
-    BSocket_AddEventHandler(s->bsock, BSOCKET_WRITE, (BSocket_handler)socket_handler, s);
-    
-    // init input
-    PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)input_handler_send, s, pg);
-    
-    // have no input packet
-    s->in_len = -1;
-    
-    DebugObject_Init(&s->d_obj);
-}
-
-void DatagramSocketSink_Free (DatagramSocketSink *s)
-{
-    DebugObject_Free(&s->d_obj);
-
-    // free input
-    PacketPassInterface_Free(&s->input);
-    
-    // remove socket event handler
-    BSocket_RemoveEventHandler(s->bsock, BSOCKET_WRITE);
-}
-
-PacketPassInterface * DatagramSocketSink_GetInput (DatagramSocketSink *s)
-{
-    DebugObject_Access(&s->d_obj);
-    
-    return &s->input;
-}
-
-void DatagramSocketSink_SetAddresses (DatagramSocketSink *s, BAddr addr, BIPAddr local_addr)
-{
-    ASSERT(!BAddr_IsInvalid(&addr))
-    BIPAddr_Assert(&local_addr);
-    DebugObject_Access(&s->d_obj);
-    
-    s->addr = addr;
-    s->local_addr = local_addr;
-}

+ 0 - 100
flowextra/DatagramSocketSink.h

@@ -1,100 +0,0 @@
-/**
- * @file DatagramSocketSink.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A {@link PacketPassInterface} sink which sends packets to a datagram socket.
- */
-
-#ifndef BADVPN_DATAGRAMSOCKETSINK_H
-#define BADVPN_DATAGRAMSOCKETSINK_H
-
-#include <stdint.h>
-
-#include <base/DebugObject.h>
-#include <system/BSocket.h>
-#include <flow/PacketPassInterface.h>
-#include <flow/FlowError.h>
-
-#define DATAGRAMSOCKETSINK_ERROR_BSOCKET 1
-#define DATAGRAMSOCKETSINK_ERROR_WRONGSIZE 2
-
-/**
- * A {@link PacketPassInterface} sink which sends packets to a datagram socket.
- */
-typedef struct {
-    FlowErrorReporter rep;
-    BSocket *bsock;
-    BAddr addr;
-    BIPAddr local_addr;
-    PacketPassInterface input;
-    int in_len;
-    uint8_t *in;
-    DebugObject d_obj;
-} DatagramSocketSink;
-
-/**
- * Initializes the sink.
- *
- * @param s the object
- * @param rep error reporting data. Error code is an int. Possible error codes:
- *              - DATAGRAMSOCKETSINK_ERROR_BSOCKET: {@link BSocket_SendToFrom} failed
- *                with an unhandled error code
- *              - DATAGRAMSOCKETSINK_ERROR_WRONGSIZE: {@link BSocket_SendToFrom} succeeded,
- *                but did not send all of the packet
- *            On error, the object will continue to operate unless it is destroyed from
- *            the error handler.
- * @param bsock datagram socket to write packets to. Registers a BSOCKET_WRITE handler which
- *              must not be registered.
- * @param mtu maximum packet size. Must be >=0.
- * @param addr remote address. Must be recognized and valid. Passed to {@link BSocket_SendToFrom}.
- * @param local_addr source address. Must be recognized.
- *                   Passed to {@link BSocket_SendToFrom}.
- * @param pg pending group
- */
-void DatagramSocketSink_Init (DatagramSocketSink *s, FlowErrorReporter rep, BSocket *bsock, int mtu, BAddr addr, BIPAddr local_addr, BPendingGroup *pg);
-
-/**
- * Frees the sink.
- *
- * @param s the object
- */
-void DatagramSocketSink_Free (DatagramSocketSink *s);
-
-/**
- * Returns the input interface.
- *
- * @param s the object
- * @return input interface
- */
-PacketPassInterface * DatagramSocketSink_GetInput (DatagramSocketSink *s);
-
-/**
- * Sets sending addresses.
- *
- * @param s the object
- * @param addr remote address. Must be recognized and valid. Passed to {@link BSocket_SendToFrom}.
- * @param local_addr source address. Must be recognized.
- *                   Passed to {@link BSocket_SendToFrom}.
- */
-void DatagramSocketSink_SetAddresses (DatagramSocketSink *s, BAddr addr, BIPAddr local_addr);
-
-#endif

+ 0 - 161
flowextra/DatagramSocketSource.c

@@ -1,161 +0,0 @@
-/**
- * @file DatagramSocketSource.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/debug.h>
-#include <base/BLog.h>
-
-#include "DatagramSocketSource.h"
-
-#include <generated/blog_channel_DatagramSocketSource.h>
-
-static void report_error (DatagramSocketSource *s, int error)
-{
-    FlowErrorReporter_ReportError(&s->rep, error);
-    return;
-}
-
-static void try_recv (DatagramSocketSource *s)
-{
-    ASSERT(s->out_have)
-    
-    int res = BSocket_RecvFromTo(s->bsock, s->out, s->mtu, &s->last_addr, &s->last_local_addr);
-    if (res < 0 && BSocket_GetError(s->bsock) == BSOCKET_ERROR_LATER) {
-        // wait for socket in socket_handler
-        BSocket_EnableEvent(s->bsock, BSOCKET_READ);
-        return;
-    }
-    
-    if (res < 0) {
-        BLog(BLOG_NOTICE, "BSocket_RecvFromTo failed (%d)", BSocket_GetError(s->bsock));
-        
-        // schedule retry
-        BPending_Set(&s->retry_job);
-        
-        // report error
-        report_error(s, DATAGRAMSOCKETSOURCE_ERROR_BSOCKET);
-        return;
-    }
-    
-    #ifndef NDEBUG
-    s->have_last_addr = 1;
-    #endif
-    
-    // finish packet
-    s->out_have = 0;
-    PacketRecvInterface_Done(&s->output, res);
-}
-
-static void output_handler_recv (DatagramSocketSource *s, uint8_t *data)
-{
-    ASSERT(!s->out_have)
-    DebugObject_Access(&s->d_obj);
-    
-    // set packet
-    s->out_have = 1;
-    s->out = data;
-    
-    try_recv(s);
-    return;
-}
-
-static void socket_handler (DatagramSocketSource *s, int event)
-{
-    ASSERT(s->out_have)
-    ASSERT(event == BSOCKET_READ)
-    DebugObject_Access(&s->d_obj);
-    
-    BSocket_DisableEvent(s->bsock, BSOCKET_READ);
-    
-    try_recv(s);
-    return;
-}
-
-static void retry_job_handler (DatagramSocketSource *s)
-{
-    ASSERT(s->out_have)
-    DebugObject_Access(&s->d_obj);
-    
-    try_recv(s);
-    return;
-}
-
-void DatagramSocketSource_Init (DatagramSocketSource *s, FlowErrorReporter rep, BSocket *bsock, int mtu, BPendingGroup *pg)
-{
-    ASSERT(mtu >= 0)
-    
-    // init arguments
-    s->rep = rep;
-    s->bsock = bsock;
-    s->mtu = mtu;
-    
-    // add socket event handler
-    BSocket_AddEventHandler(s->bsock, BSOCKET_READ, (BSocket_handler)socket_handler, s);
-    
-    // init output
-    PacketRecvInterface_Init(&s->output, mtu, (PacketRecvInterface_handler_recv)output_handler_recv, s, pg);
-    
-    // have no output packet
-    s->out_have = 0;
-    
-    // init retry job
-    BPending_Init(&s->retry_job, pg, (BPending_handler)retry_job_handler, s);
-    
-    DebugObject_Init(&s->d_obj);
-    #ifndef NDEBUG
-    s->have_last_addr = 0;
-    #endif
-}
-
-void DatagramSocketSource_Free (DatagramSocketSource *s)
-{
-    DebugObject_Free(&s->d_obj);
-    
-    // free retry job
-    BPending_Free(&s->retry_job);
-    
-    // free output
-    PacketRecvInterface_Free(&s->output);
-    
-    // remove socket event handler
-    BSocket_RemoveEventHandler(s->bsock, BSOCKET_READ);
-}
-
-PacketRecvInterface * DatagramSocketSource_GetOutput (DatagramSocketSource *s)
-{
-    DebugObject_Access(&s->d_obj);
-    
-    return &s->output;
-}
-
-void DatagramSocketSource_GetLastAddresses (DatagramSocketSource *s, BAddr *addr, BIPAddr *local_addr)
-{
-    ASSERT(s->have_last_addr)
-    DebugObject_Access(&s->d_obj);
-    
-    if (addr) {
-        *addr = s->last_addr;
-    }
-    
-    if (local_addr) {
-        *local_addr = s->last_local_addr;
-    }
-}

+ 0 - 102
flowextra/DatagramSocketSource.h

@@ -1,102 +0,0 @@
-/**
- * @file DatagramSocketSource.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A {@link PacketRecvInterface} source which receives packets from a datagram socket.
- */
-
-#ifndef BADVPN_DATAGRAMSOCKETSOURCE_H
-#define BADVPN_DATAGRAMSOCKETSOURCE_H
-
-#include <stdint.h>
-
-#include <base/DebugObject.h>
-#include <system/BSocket.h>
-#include <base/BPending.h>
-#include <flow/PacketRecvInterface.h>
-#include <flow/FlowError.h>
-
-#define DATAGRAMSOCKETSOURCE_ERROR_BSOCKET 1
-
-/**
- * A {@link PacketRecvInterface} source which receives packets from a datagram socket.
- */
-typedef struct {
-    FlowErrorReporter rep;
-    BSocket *bsock;
-    int mtu;
-    PacketRecvInterface output;
-    int out_have;
-    uint8_t *out;
-    BAddr last_addr;
-    BIPAddr last_local_addr;
-    BPending retry_job;
-    DebugObject d_obj;
-    #ifndef NDEBUG
-    int have_last_addr;
-    #endif
-} DatagramSocketSource;
-
-/**
- * Initializes the object.
- *
- * @param s the object
- * @param rep error reporting data. Error code is an int. Possible error codes:
- *              - DATAGRAMSOCKETSOURCE_ERROR_BSOCKET: {@link BSocket_RecvFromTo} failed
- *                with an unhandled error code
- *            On error, the object will continue to operate unless it is destroyed from
- *            the error handler.
- * @param bsock datagram socket to read data from. The BSOCKET_READ event must be disabled.
- * *            Takes over reading on the socket.
- * @param mtu maximum packet size. Must be >=0.
- * @param pg pending group
- */
-void DatagramSocketSource_Init (DatagramSocketSource *s, FlowErrorReporter rep, BSocket *bsock, int mtu, BPendingGroup *pg);
-
-/**
- * Frees the object.
- *
- * @param s the object
- */
-void DatagramSocketSource_Free (DatagramSocketSource *s);
-
-/**
- * Returns the output interface.
- *
- * @param s the object
- * @return output interface
- */
-PacketRecvInterface * DatagramSocketSource_GetOutput (DatagramSocketSource *s);
-
-/**
- * Returns the remote and local address of the last received packet.
- * At least one packet must have been received.
- *
- * @param s the object
- * @param addr where to put the remote address, if not NULL. The returned address
- *             will be valid.
- * @param local_addr where to put the local address, if not NULL. The returned
- *                   address may be an invalid address.
- */
-void DatagramSocketSource_GetLastAddresses (DatagramSocketSource *s, BAddr *addr, BIPAddr *local_addr);
-
-#endif

+ 0 - 122
flowextra/StreamSocketSink.c

@@ -1,122 +0,0 @@
-/**
- * @file StreamSocketSink.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/debug.h>
-#include <base/BLog.h>
-
-#include "StreamSocketSink.h"
-
-#include <generated/blog_channel_StreamSocketSink.h>
-
-static void report_error (StreamSocketSink *s, int error)
-{
-    DEBUGERROR(&s->d_err, FlowErrorReporter_ReportError(&s->rep, error))
-}
-
-static void try_send (StreamSocketSink *s)
-{
-    ASSERT(s->in_len > 0)
-    
-    int res = BSocket_Send(s->bsock, s->in, s->in_len);
-    if (res < 0 && BSocket_GetError(s->bsock) == BSOCKET_ERROR_LATER) {
-        // wait for socket in socket_handler
-        BSocket_EnableEvent(s->bsock, BSOCKET_WRITE);
-        return;
-    }
-    
-    if (res < 0) {
-        BLog(BLOG_NOTICE, "BSocket_Send failed (%d)", BSocket_GetError(s->bsock));
-        report_error(s, STREAMSOCKETSINK_ERROR_BSOCKET);
-        return;
-    }
-    
-    ASSERT(res > 0)
-    ASSERT(res <= s->in_len)
-    
-    // finish packet
-    s->in_len = -1;
-    StreamPassInterface_Done(&s->input, res);
-}
-
-static void input_handler_send (StreamSocketSink *s, uint8_t *data, int data_len)
-{
-    ASSERT(data_len > 0)
-    ASSERT(s->in_len == -1)
-    DebugObject_Access(&s->d_obj);
-    
-    // set packet
-    s->in_len = data_len;
-    s->in = data;
-    
-    try_send(s);
-    return;
-}
-
-static void socket_handler (StreamSocketSink *s, int event)
-{
-    ASSERT(s->in_len > 0)
-    ASSERT(event == BSOCKET_WRITE)
-    DebugObject_Access(&s->d_obj);
-    
-    BSocket_DisableEvent(s->bsock, BSOCKET_WRITE);
-    
-    try_send(s);
-    return;
-}
-
-void StreamSocketSink_Init (StreamSocketSink *s, FlowErrorReporter rep, BSocket *bsock, BPendingGroup *pg)
-{
-    // init arguments
-    s->rep = rep;
-    s->bsock = bsock;
-    
-    // add socket event handler
-    BSocket_AddEventHandler(s->bsock, BSOCKET_WRITE, (BSocket_handler)socket_handler, s);
-    
-    // init input
-    StreamPassInterface_Init(&s->input, (StreamPassInterface_handler_send)input_handler_send, s, pg);
-    
-    // have no input packet
-    s->in_len = -1;
-    
-    DebugObject_Init(&s->d_obj);
-    DebugError_Init(&s->d_err, BReactor_PendingGroup(BSocket_Reactor(s->bsock)));
-}
-
-void StreamSocketSink_Free (StreamSocketSink *s)
-{
-    DebugError_Free(&s->d_err);
-    DebugObject_Free(&s->d_obj);
-    
-    // free input
-    StreamPassInterface_Free(&s->input);
-    
-    // remove socket event handler
-    BSocket_RemoveEventHandler(s->bsock, BSOCKET_WRITE);
-}
-
-StreamPassInterface * StreamSocketSink_GetInput (StreamSocketSink *s)
-{
-    DebugObject_Access(&s->d_obj);
-    
-    return &s->input;
-}

+ 0 - 82
flowextra/StreamSocketSink.h

@@ -1,82 +0,0 @@
-/**
- * @file StreamSocketSink.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A {@link StreamPassInterface} sink which sends data to a stream socket.
- */
-
-#ifndef BADVPN_STREAMSOCKETSINK_H
-#define BADVPN_STREAMSOCKETSINK_H
-
-#include <stdint.h>
-
-#include <misc/debugerror.h>
-#include <base/DebugObject.h>
-#include <system/BSocket.h>
-#include <flow/StreamPassInterface.h>
-#include <flow/FlowError.h>
-
-#define STREAMSOCKETSINK_ERROR_BSOCKET 1
-
-/**
- * A {@link StreamPassInterface} sink which sends data to a stream socket.
- */
-typedef struct {
-    FlowErrorReporter rep;
-    BSocket *bsock;
-    StreamPassInterface input;
-    int in_len;
-    uint8_t *in;
-    DebugObject d_obj;
-    DebugError d_err;
-} StreamSocketSink;
-
-/**
- * Initializes the sink.
- *
- * @param s the object
- * @param rep error reporting data. Error code is an int. Possible error codes:
- *              - STREAMSOCKETSINK_ERROR_BSOCKET: {@link BSocket_Send} failed
- *                with an unhandled error code
- *            The object must be freed from the error handler.
- * @param bsock stream socket to write data to. Registers a BSOCKET_WRITE handler which
- *              must not be registered.
- * @param pg pending group
- */
-void StreamSocketSink_Init (StreamSocketSink *s, FlowErrorReporter rep, BSocket *bsock, BPendingGroup *pg);
-
-/**
- * Frees the sink.
- *
- * @param s the object
- */
-void StreamSocketSink_Free (StreamSocketSink *s);
-
-/**
- * Returns the input interface.
- *
- * @param s the object
- * @return input interface
- */
-StreamPassInterface * StreamSocketSink_GetInput (StreamSocketSink *s);
-
-#endif

+ 0 - 128
flowextra/StreamSocketSource.c

@@ -1,128 +0,0 @@
-/**
- * @file StreamSocketSource.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/debug.h>
-#include <base/BLog.h>
-
-#include "StreamSocketSource.h"
-
-#include <generated/blog_channel_StreamSocketSource.h>
-
-static void report_error (StreamSocketSource *s, int error)
-{
-    DEBUGERROR(&s->d_err, FlowErrorReporter_ReportError(&s->rep, error))
-}
-
-static void try_recv (StreamSocketSource *s)
-{
-    ASSERT(s->out_avail > 0)
-    
-    int res = BSocket_Recv(s->bsock, s->out, s->out_avail);
-    if (res < 0 && BSocket_GetError(s->bsock) == BSOCKET_ERROR_LATER) {
-        // wait for socket in socket_handler
-        BSocket_EnableEvent(s->bsock, BSOCKET_READ);
-        return;
-    }
-    
-    if (res < 0) {
-        BLog(BLOG_NOTICE, "BSocket_Recv failed (%d)", BSocket_GetError(s->bsock));
-        report_error(s, STREAMSOCKETSOURCE_ERROR_BSOCKET);
-        return;
-    }
-    
-    if (res == 0) {
-        BLog(BLOG_NOTICE, "Connection closed");
-        report_error(s, STREAMSOCKETSOURCE_ERROR_CLOSED);
-        return;
-    }
-    
-    ASSERT(res > 0)
-    ASSERT(res <= s->out_avail)
-    
-    // finish packet
-    s->out_avail = -1;
-    StreamRecvInterface_Done(&s->output, res);
-}
-
-static void output_handler_recv (StreamSocketSource *s, uint8_t *data, int data_avail)
-{
-    ASSERT(data_avail > 0)
-    ASSERT(s->out_avail == -1)
-    DebugObject_Access(&s->d_obj);
-    
-    // set packet
-    s->out_avail = data_avail;
-    s->out = data;
-    
-    try_recv(s);
-    return;
-}
-
-static void socket_handler (StreamSocketSource *s, int event)
-{
-    ASSERT(s->out_avail > 0)
-    ASSERT(event == BSOCKET_READ)
-    DebugObject_Access(&s->d_obj);
-    
-    BSocket_DisableEvent(s->bsock, BSOCKET_READ);
-    
-    try_recv(s);
-    return;
-}
-
-void StreamSocketSource_Init (StreamSocketSource *s, FlowErrorReporter rep, BSocket *bsock, BPendingGroup *pg)
-{
-    // init arguments
-    s->rep = rep;
-    s->bsock = bsock;
-    
-    // add socket event handler
-    BSocket_AddEventHandler(s->bsock, BSOCKET_READ, (BSocket_handler)socket_handler, s);
-    
-    // init output
-    StreamRecvInterface_Init(&s->output, (StreamRecvInterface_handler_recv)output_handler_recv, s, pg);
-    
-    // have no output packet
-    s->out_avail = -1;
-    
-    DebugObject_Init(&s->d_obj);
-    DebugError_Init(&s->d_err, BReactor_PendingGroup(BSocket_Reactor(s->bsock)));
-}
-
-void StreamSocketSource_Free (StreamSocketSource *s)
-{
-    DebugError_Free(&s->d_err);
-    DebugObject_Free(&s->d_obj);
-    
-    // free output
-    StreamRecvInterface_Free(&s->output);
-    
-    // remove socket event handler
-    BSocket_RemoveEventHandler(s->bsock, BSOCKET_READ);
-}
-
-StreamRecvInterface * StreamSocketSource_GetOutput (StreamSocketSource *s)
-{
-    DebugObject_Access(&s->d_obj);
-    
-    return &s->output;
-}

+ 0 - 84
flowextra/StreamSocketSource.h

@@ -1,84 +0,0 @@
-/**
- * @file StreamSocketSource.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A {@link StreamRecvInterface} source which receives data from a stream socket.
- */
-
-#ifndef BADVPN_STREAMSOCKETSOURCE_H
-#define BADVPN_STREAMSOCKETSOURCE_H
-
-#include <stdint.h>
-
-#include <misc/debugerror.h>
-#include <base/DebugObject.h>
-#include <system/BSocket.h>
-#include <flow/StreamRecvInterface.h>
-#include <flow/FlowError.h>
-
-#define STREAMSOCKETSOURCE_ERROR_CLOSED 0
-#define STREAMSOCKETSOURCE_ERROR_BSOCKET 1
-
-/**
- * A {@link StreamRecvInterface} source which receives data from a stream socket.
- */
-typedef struct {
-    FlowErrorReporter rep;
-    BSocket *bsock;
-    StreamRecvInterface output;
-    int out_avail;
-    uint8_t *out;
-    DebugObject d_obj;
-    DebugError d_err;
-} StreamSocketSource;
-
-/**
- * Initializes the source.
- *
- * @param s the object
- * @param rep error reporting data. Error code is an int. Possible error codes:
- *              - STREAMSOCKETSOURCE_ERROR_CLOSED: {@link BSocket_Recv} returned 0
- *              - STREAMSOCKETSOURCE_ERROR_BSOCKET: {@link BSocket_Recv} failed
- *                with an unhandled error code
- *            The object must be freed from the error handler.
- * @param bsock stream socket to read data from. Registers a BSOCKET_READ handler which
- *              must not be registered.
- * @param pg pending group
- */
-void StreamSocketSource_Init (StreamSocketSource *s, FlowErrorReporter rep, BSocket *bsock, BPendingGroup *pg);
-
-/**
- * Frees the source.
- *
- * @param s the object
- */
-void StreamSocketSource_Free (StreamSocketSource *s);
-
-/**
- * Returns the output interface.
- *
- * @param s the object
- * @return output interface
- */
-StreamRecvInterface * StreamSocketSource_GetOutput (StreamSocketSource *s);
-
-#endif

+ 1 - 1
generated/blog_channel_StreamSocketSink.h → generated/blog_channel_BConnection.h

@@ -1,4 +1,4 @@
 #ifdef BLOG_CURRENT_CHANNEL
 #undef BLOG_CURRENT_CHANNEL
 #endif
-#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_StreamSocketSink
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BConnection

+ 1 - 1
generated/blog_channel_BSocketPRFileDesc.h → generated/blog_channel_BDatagram.h

@@ -1,4 +1,4 @@
 #ifdef BLOG_CURRENT_CHANNEL
 #undef BLOG_CURRENT_CHANNEL
 #endif
-#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSocketPRFileDesc
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDatagram

+ 1 - 1
generated/blog_channel_BSocket.h → generated/blog_channel_BNetwork.h

@@ -1,4 +1,4 @@
 #ifdef BLOG_CURRENT_CHANNEL
 #undef BLOG_CURRENT_CHANNEL
 #endif
-#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSocket
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BNetwork

+ 1 - 1
generated/blog_channel_DatagramSocketSink.h → generated/blog_channel_BSSLConnection.h

@@ -1,4 +1,4 @@
 #ifdef BLOG_CURRENT_CHANNEL
 #undef BLOG_CURRENT_CHANNEL
 #endif
-#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DatagramSocketSink
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSSLConnection

+ 0 - 4
generated/blog_channel_DatagramSocketSource.h

@@ -1,4 +0,0 @@
-#ifdef BLOG_CURRENT_CHANNEL
-#undef BLOG_CURRENT_CHANNEL
-#endif
-#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DatagramSocketSource

+ 0 - 4
generated/blog_channel_StreamSocketSource.h

@@ -1,4 +0,0 @@
-#ifdef BLOG_CURRENT_CHANNEL
-#undef BLOG_CURRENT_CHANNEL
-#endif
-#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_StreamSocketSource

+ 31 - 33
generated/blog_channels_defines.h

@@ -51,36 +51,34 @@
 #define BLOG_CHANNEL_NCDIfConfig 50
 #define BLOG_CHANNEL_BUnixSignal 51
 #define BLOG_CHANNEL_BProcess 52
-#define BLOG_CHANNEL_StreamSocketSink 53
-#define BLOG_CHANNEL_StreamSocketSource 54
-#define BLOG_CHANNEL_DatagramSocketSink 55
-#define BLOG_CHANNEL_DatagramSocketSource 56
-#define BLOG_CHANNEL_PRStreamSink 57
-#define BLOG_CHANNEL_PRStreamSource 58
-#define BLOG_CHANNEL_BSocketPRFileDesc 59
-#define BLOG_CHANNEL_PacketProtoDecoder 60
-#define BLOG_CHANNEL_DPRelay 61
-#define BLOG_CHANNEL_BThreadWork 62
-#define BLOG_CHANNEL_DPReceive 63
-#define BLOG_CHANNEL_BInputProcess 64
-#define BLOG_CHANNEL_NCDUdevMonitorParser 65
-#define BLOG_CHANNEL_NCDUdevMonitor 66
-#define BLOG_CHANNEL_NCDUdevCache 67
-#define BLOG_CHANNEL_NCDUdevManager 68
-#define BLOG_CHANNEL_BTime 69
-#define BLOG_CHANNEL_BSocket 70
-#define BLOG_CHANNEL_BEncryption 71
-#define BLOG_CHANNEL_SPProtoDecoder 72
-#define BLOG_CHANNEL_LineBuffer 73
-#define BLOG_CHANNEL_BTap 74
-#define BLOG_CHANNEL_lwip 75
-#define BLOG_CHANNEL_NCDConfigParser 76
-#define BLOG_CHANNEL_nsskey 77
-#define BLOG_CHANNEL_addr 78
-#define BLOG_CHANNEL_PasswordListener 79
-#define BLOG_CHANNEL_NCDInterfaceMonitor 80
-#define BLOG_CHANNEL_NCDRfkillMonitor 81
-#define BLOG_CHANNEL_udpgw 82
-#define BLOG_CHANNEL_UdpGwClient 83
-#define BLOG_CHANNEL_SocksUdpGwClient 84
-#define BLOG_NUM_CHANNELS 85
+#define BLOG_CHANNEL_PRStreamSink 53
+#define BLOG_CHANNEL_PRStreamSource 54
+#define BLOG_CHANNEL_PacketProtoDecoder 55
+#define BLOG_CHANNEL_DPRelay 56
+#define BLOG_CHANNEL_BThreadWork 57
+#define BLOG_CHANNEL_DPReceive 58
+#define BLOG_CHANNEL_BInputProcess 59
+#define BLOG_CHANNEL_NCDUdevMonitorParser 60
+#define BLOG_CHANNEL_NCDUdevMonitor 61
+#define BLOG_CHANNEL_NCDUdevCache 62
+#define BLOG_CHANNEL_NCDUdevManager 63
+#define BLOG_CHANNEL_BTime 64
+#define BLOG_CHANNEL_BEncryption 65
+#define BLOG_CHANNEL_SPProtoDecoder 66
+#define BLOG_CHANNEL_LineBuffer 67
+#define BLOG_CHANNEL_BTap 68
+#define BLOG_CHANNEL_lwip 69
+#define BLOG_CHANNEL_NCDConfigParser 70
+#define BLOG_CHANNEL_nsskey 71
+#define BLOG_CHANNEL_addr 72
+#define BLOG_CHANNEL_PasswordListener 73
+#define BLOG_CHANNEL_NCDInterfaceMonitor 74
+#define BLOG_CHANNEL_NCDRfkillMonitor 75
+#define BLOG_CHANNEL_udpgw 76
+#define BLOG_CHANNEL_UdpGwClient 77
+#define BLOG_CHANNEL_SocksUdpGwClient 78
+#define BLOG_CHANNEL_BNetwork 79
+#define BLOG_CHANNEL_BConnection 80
+#define BLOG_CHANNEL_BSSLConnection 81
+#define BLOG_CHANNEL_BDatagram 82
+#define BLOG_NUM_CHANNELS 83

+ 4 - 6
generated/blog_channels_list.h

@@ -51,13 +51,8 @@
 {.name = "NCDIfConfig", .loglevel = 4},
 {.name = "BUnixSignal", .loglevel = 4},
 {.name = "BProcess", .loglevel = 4},
-{.name = "StreamSocketSink", .loglevel = 4},
-{.name = "StreamSocketSource", .loglevel = 4},
-{.name = "DatagramSocketSink", .loglevel = 4},
-{.name = "DatagramSocketSource", .loglevel = 4},
 {.name = "PRStreamSink", .loglevel = 4},
 {.name = "PRStreamSource", .loglevel = 4},
-{.name = "BSocketPRFileDesc", .loglevel = 4},
 {.name = "PacketProtoDecoder", .loglevel = 4},
 {.name = "DPRelay", .loglevel = 4},
 {.name = "BThreadWork", .loglevel = 4},
@@ -68,7 +63,6 @@
 {.name = "NCDUdevCache", .loglevel = 4},
 {.name = "NCDUdevManager", .loglevel = 4},
 {.name = "BTime", .loglevel = 4},
-{.name = "BSocket", .loglevel = 4},
 {.name = "BEncryption", .loglevel = 4},
 {.name = "SPProtoDecoder", .loglevel = 4},
 {.name = "LineBuffer", .loglevel = 4},
@@ -83,3 +77,7 @@
 {.name = "udpgw", .loglevel = 4},
 {.name = "UdpGwClient", .loglevel = 4},
 {.name = "SocksUdpGwClient", .loglevel = 4},
+{.name = "BNetwork", .loglevel = 4},
+{.name = "BConnection", .loglevel = 4},
+{.name = "BSSLConnection", .loglevel = 4},
+{.name = "BDatagram", .loglevel = 4},

+ 21 - 44
inputprocess/BInputProcess.c

@@ -28,53 +28,25 @@
 
 #include <generated/blog_channel_BInputProcess.h>
 
-static int init_pipe (BInputProcess *o, int pipe_fd);
-static void free_pipe (BInputProcess *o);
-static void pipe_source_handler_error (BInputProcess *o, int component, int code);
+static void connection_handler (BInputProcess *o, int event);
 static void process_handler (BInputProcess *o, int normally, uint8_t normally_exit_status);
 
-int init_pipe (BInputProcess *o, int pipe_fd)
-{
-    // init socket
-    if (BSocket_InitPipe(&o->pipe_sock, o->reactor, pipe_fd) < 0) {
-        BLog(BLOG_ERROR, "BSocket_InitPipe failed");
-        goto fail0;
-    }
-    
-    // init domain
-    FlowErrorDomain_Init(&o->pipe_domain, (FlowErrorDomain_handler)pipe_source_handler_error, o);
-    
-    // init source
-    StreamSocketSource_Init(&o->pipe_source, FlowErrorReporter_Create(&o->pipe_domain, 0), &o->pipe_sock, BReactor_PendingGroup(o->reactor));
-    
-    return 1;
-    
-fail0:
-    return 0;
-}
-
-void free_pipe (BInputProcess *o)
-{
-    // free source
-    StreamSocketSource_Free(&o->pipe_source);
-    
-    // free socket
-    BSocket_Free(&o->pipe_sock);
-}
-
-void pipe_source_handler_error (BInputProcess *o, int component, int code)
+void connection_handler (BInputProcess *o, int event)
 {
     DebugObject_Access(&o->d_obj);
     ASSERT(o->pipe_fd >= 0)
     
-    if (code == STREAMSOCKETSOURCE_ERROR_CLOSED) {
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
         BLog(BLOG_INFO, "pipe closed");
     } else {
         BLog(BLOG_ERROR, "pipe error");
     }
     
-    // free pipe reading
-    free_pipe(o);
+    // free pipe connection read interface
+    BConnection_RecvAsync_Free(&o->pipe_con);
+    
+    // free pipe connection
+    BConnection_Free(&o->pipe_con);
     
     // close pipe read end
     ASSERT_FORCE(close(o->pipe_fd) == 0)
@@ -83,7 +55,7 @@ void pipe_source_handler_error (BInputProcess *o, int component, int code)
     o->pipe_fd = -1;
     
     // call closed handler
-    o->handler_closed(o->user, (code != STREAMSOCKETSOURCE_ERROR_CLOSED));
+    o->handler_closed(o->user, (event != BCONNECTION_EVENT_RECVCLOSED));
     return;
 }
 
@@ -122,11 +94,15 @@ int BInputProcess_Init (BInputProcess *o, BReactor *reactor, BProcessManager *ma
         goto fail0;
     }
     
-    // init pipe reading
-    if (!init_pipe(o, pipefds[0])) {
+    // init pipe connection
+    if (!BConnection_Init(&o->pipe_con, BCONNECTION_SOURCE_PIPE(pipefds[0]), o->reactor, o, (BConnection_handler)connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
         goto fail1;
     }
     
+    // init pipe connection read interface
+    BConnection_RecvAsync_Init(&o->pipe_con);
+    
     // remember pipe fds
     o->pipe_fd = pipefds[0];
     o->pipe_write_fd = pipefds[1];
@@ -137,8 +113,6 @@ int BInputProcess_Init (BInputProcess *o, BReactor *reactor, BProcessManager *ma
     DebugObject_Init(&o->d_obj);
     return 1;
     
-fail2:
-    free_pipe(o);
 fail1:
     ASSERT_FORCE(close(pipefds[0]) == 0)
     ASSERT_FORCE(close(pipefds[1]) == 0)
@@ -161,8 +135,11 @@ void BInputProcess_Free (BInputProcess *o)
     }
     
     if (o->pipe_fd >= 0) {
-        // free pipe reading
-        free_pipe(o);
+        // free pipe connection read interface
+        BConnection_RecvAsync_Free(&o->pipe_con);
+        
+        // free pipe connection
+        BConnection_Free(&o->pipe_con);
         
         // close pipe read end
         ASSERT_FORCE(close(o->pipe_fd) == 0)
@@ -220,5 +197,5 @@ StreamRecvInterface * BInputProcess_GetInput (BInputProcess *o)
     DebugObject_Access(&o->d_obj);
     ASSERT(o->pipe_fd >= 0)
     
-    return StreamSocketSource_GetOutput(&o->pipe_source);
+    return BConnection_RecvAsync_GetIf(&o->pipe_con);
 }

+ 2 - 5
inputprocess/BInputProcess.h

@@ -25,9 +25,8 @@
 
 #include <misc/debug.h>
 #include <base/DebugObject.h>
-#include <system/BSocket.h>
+#include <system/BConnection.h>
 #include <process/BProcess.h>
-#include <flowextra/StreamSocketSource.h>
 
 typedef void (*BInputProcess_handler_terminated) (void *user, int normally, uint8_t normally_exit_status);
 typedef void (*BInputProcess_handler_closed) (void *user, int is_error);
@@ -43,9 +42,7 @@ typedef struct {
     int have_process;
     BProcess process;
     int pipe_fd;
-    BSocket pipe_sock;
-    FlowErrorDomain pipe_domain;
-    StreamSocketSource pipe_source;
+    BConnection pipe_con;
     DebugObject d_obj;
 } BInputProcess;
 

+ 3 - 4
misc/sslsocket.h

@@ -29,14 +29,13 @@
 
 #include <prio.h>
 
-#include <system/BSocket.h>
-#include <nspr_support/BPRFileDesc.h>
+#include <system/BConnection.h>
+#include <nspr_support/BSSLConnection.h>
 
 typedef struct {
-    BSocket sock;
+    BConnection con;
     PRFileDesc bottom_prfd;
     PRFileDesc *ssl_prfd;
-    BPRFileDesc ssl_bprfd;
 } sslsocket;
 
 #endif

+ 16 - 2
ncd/modules/net_ipv4_dhcp.c

@@ -55,6 +55,8 @@ struct instance {
     int up;
 };
 
+static void instance_free (struct instance *o);
+
 static void dhcp_handler (struct instance *o, int event)
 {
     switch (event) {
@@ -70,6 +72,12 @@ static void dhcp_handler (struct instance *o, int event)
             NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
         } break;
         
+        case BDHCPCLIENT_EVENT_ERROR: {
+            NCDModuleInst_Backend_SetError(o->i);
+            instance_free(o);
+            return;
+        } break;
+        
         default: ASSERT(0);
     }
 }
@@ -117,9 +125,8 @@ fail0:
     NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
-static void func_die (void *vo)
+static void instance_free (struct instance *o)
 {
-    struct instance *o = vo;
     NCDModuleInst *i = o->i;
     
     // free DHCP
@@ -131,6 +138,13 @@ static void func_die (void *vo)
     NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
 }
 
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    
+    instance_free(o);
+}
+
 static int func_getvar (void *vo, const char *name, NCDValue *out)
 {
     struct instance *o = vo;

+ 4 - 4
ncd/ncd.c

@@ -38,7 +38,7 @@
 #include <base/BLog.h>
 #include <system/BReactor.h>
 #include <system/BSignal.h>
-#include <system/BSocket.h>
+#include <system/BConnection.h>
 #include <process/BProcess.h>
 #include <udevmonitor/NCDUdevManager.h>
 #include <ncdconfig/NCDConfigParser.h>
@@ -222,9 +222,9 @@ int main (int argc, char **argv)
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
-    // initialize sockets
-    if (BSocket_GlobalInit() < 0) {
-        BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
+    // initialize network
+    if (!BNetwork_GlobalInit()) {
+        BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
         goto fail1;
     }
     

+ 0 - 327
nspr_support/BPRFileDesc.c

@@ -1,327 +0,0 @@
-/**
- * @file BPRFileDesc.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 <stdlib.h>
-
-#include <misc/offset.h>
-#include <misc/debug.h>
-
-#include <nspr_support/BPRFileDesc.h>
-
-#define HANDLER_READ 0
-#define HANDLER_WRITE 1
-#define NUM_EVENTS 2
-
-static int get_event_index (PRInt16 event);
-static void init_handlers (BPRFileDesc *obj);
-static int get_bsocket_events (PRUint16 pr_events);
-static void init_bottom (BPRFileDesc *obj);
-static void free_bottom (BPRFileDesc *obj);
-static void update_bottom (BPRFileDesc *obj);
-static void set_bottom_events (BPRFileDesc *obj, PRInt16 new_events);
-static void socket_handler (BPRFileDesc *obj, int event);
-
-int get_event_index (PRInt16 event)
-{
-    switch (event) {
-        case PR_POLL_READ:
-            return HANDLER_READ;
-        case PR_POLL_WRITE:
-            return HANDLER_WRITE;
-        default:
-            ASSERT(0)
-            return 0;
-    }
-}
-
-static PRInt16 handler_events[] = {
-    [HANDLER_READ] = PR_POLL_READ,
-    [HANDLER_WRITE] = PR_POLL_WRITE
-};
-
-void init_handlers (BPRFileDesc *obj)
-{
-    int i;
-    for (i = 0; i < NUM_EVENTS; i++) {
-        obj->handlers[i] = NULL;
-    }
-}
-
-void work_events (BPRFileDesc *o)
-{
-    ASSERT(o->dispatching)
-    ASSERT(o->current_event_index >= 0)
-    ASSERT(o->current_event_index <= NUM_EVENTS)
-    ASSERT(((o->ready_events)&~(o->waitEvents)) == 0)
-    
-    while (o->current_event_index < NUM_EVENTS) {
-        // get event
-        int ev_index = o->current_event_index;
-        PRInt16 ev_mask = handler_events[ev_index];
-        int ev_dispatch = (o->ready_events&ev_mask);
-        
-        // jump to next event, clear this event
-        o->current_event_index++;
-        o->ready_events &= ~ev_mask;
-        
-        if (ev_dispatch) {
-            // schedule job that will call further handlers, or update bottom events at the end
-            BPending_Set(&o->job);
-            
-            // disable event before dispatching it
-            BPRFileDesc_DisableEvent(o, ev_mask);
-            
-            // dispatch this event
-            o->handlers[ev_index](o->handlers_user[ev_index], ev_mask);
-            return;
-        }
-    }
-    
-    ASSERT(!o->ready_events)
-    
-    o->dispatching = 0;
-    
-    // recalculate bottom events
-    update_bottom(o);
-}
-
-void job_handler (BPRFileDesc *o)
-{
-    ASSERT(o->dispatching)
-    ASSERT(o->current_event_index >= 0)
-    ASSERT(o->current_event_index <= NUM_EVENTS)
-    ASSERT(((o->ready_events)&~(o->waitEvents)) == 0) // BPRFileDesc_DisableEvent clears events from ready_events
-    DebugObject_Access(&o->d_obj);
-    
-    work_events(o);
-    return;
-}
-
-void dispatch_events (BPRFileDesc *o, PRInt16 events)
-{
-    ASSERT(!o->dispatching)
-    ASSERT((events&~(o->waitEvents)) == 0)
-    
-    o->dispatching = 1;
-    o->ready_events = events;
-    o->current_event_index = 0;
-    
-    work_events(o);
-    return;
-}
-
-int get_bsocket_events (PRUint16 pr_events)
-{
-    int res = 0;
-    
-    if (pr_events&PR_POLL_READ) {
-        res |= BSOCKET_READ;
-    }
-    if (pr_events&PR_POLL_WRITE) {
-        res |= BSOCKET_WRITE;
-    }
-    
-    return res;
-}
-
-void init_bottom (BPRFileDesc *obj)
-{
-    PRFileDesc *layer = obj->prfd;
-    do {
-        if (layer->identity == bsocketprfiledesc_identity) {
-            obj->bottom_type = BPRFILEDESC_BOTTOM_BSOCKET;
-            obj->bottom = layer;
-            BSocket_AddGlobalEventHandler((BSocket *)obj->bottom->secret, (BSocket_handler)socket_handler, obj);
-            return;
-        }
-        layer = layer->lower;
-    } while (layer);
-    
-    ASSERT(0)
-}
-
-void free_bottom (BPRFileDesc *obj)
-{
-    switch (obj->bottom_type) {
-        case BPRFILEDESC_BOTTOM_BSOCKET:
-            BSocket_RemoveGlobalEventHandler((BSocket *)obj->bottom->secret);
-            break;
-        default:
-            ASSERT(0)
-            break;
-    }
-}
-
-void update_bottom (BPRFileDesc *obj)
-{
-    // calculate bottom events
-    PRInt16 new_bottom_events = 0;
-    PRInt16 new_flags;
-    PRInt16 out_flags;
-    if (obj->waitEvents&PR_POLL_READ) {
-        new_flags = obj->prfd->methods->poll(obj->prfd, PR_POLL_READ, &out_flags);
-        if ((new_flags&out_flags) == 0) {
-            new_bottom_events |= new_flags;
-        }
-    }
-    if (obj->waitEvents&PR_POLL_WRITE) {
-        new_flags = obj->prfd->methods->poll(obj->prfd, PR_POLL_WRITE, &out_flags);
-        if ((new_flags&out_flags) == 0) {
-            new_bottom_events |= new_flags;
-        }
-    }
-    
-    switch (obj->bottom_type) {
-        case BPRFILEDESC_BOTTOM_BSOCKET:
-            BSocket_SetGlobalEvents((BSocket *)obj->bottom->secret, get_bsocket_events(new_bottom_events));
-            break;
-        default:
-            ASSERT(0)
-            break;
-    }
-}
-
-void socket_handler (BPRFileDesc *obj, int events)
-{
-    ASSERT(!obj->dispatching)
-    DebugObject_Access(&obj->d_obj);
-    
-    // dispatch all events the user is waiting for, as there is
-    // no way to know which of those are ready
-    dispatch_events(obj, obj->waitEvents);
-    return;
-}
-
-void BPRFileDesc_Init (BPRFileDesc *obj, PRFileDesc *prfd)
-{
-    obj->prfd = prfd;
-    init_handlers(obj);
-    obj->waitEvents = 0;
-    obj->dispatching = 0;
-    obj->ready_events = 0; // just initialize it so we can clear them safely from BPRFileDesc_DisableEvent
-    
-    // init bottom
-    init_bottom(obj);
-    
-    // init job
-    BPending_Init(&obj->job, BReactor_PendingGroup(BSocket_Reactor((BSocket *)obj->bottom->secret)), (BPending_handler)job_handler, obj);
-    
-    DebugObject_Init(&obj->d_obj);
-}
-
-void BPRFileDesc_Free (BPRFileDesc *obj)
-{
-    DebugObject_Free(&obj->d_obj);
-    
-    // free job
-    BPending_Free(&obj->job);
-    
-    // free bottom
-    free_bottom(obj);
-}
-
-void BPRFileDesc_AddEventHandler (BPRFileDesc *obj, PRInt16 event, BPRFileDesc_handler handler, void *user)
-{
-    ASSERT(handler)
-    DebugObject_Access(&obj->d_obj);
-    
-    // get index
-    int index = get_event_index(event);
-    
-    // event must not have handler
-    ASSERT(!obj->handlers[index])
-    
-    // change handler
-    obj->handlers[index] = handler;
-    obj->handlers_user[index] = user;
-}
-
-void BPRFileDesc_RemoveEventHandler (BPRFileDesc *obj, PRInt16 event)
-{
-    DebugObject_Access(&obj->d_obj);
-    
-    // get index
-    int index = get_event_index(event);
-    
-    // event must have handler
-    ASSERT(obj->handlers[index])
-    
-    // disable event if enabled
-    if (obj->waitEvents&event) {
-        BPRFileDesc_DisableEvent(obj, event);
-    }
-    
-    // change handler
-    obj->handlers[index] = NULL;
-}
-
-void BPRFileDesc_EnableEvent (BPRFileDesc *obj, PRInt16 event)
-{
-    DebugObject_Access(&obj->d_obj);
-    
-    // get index
-    int index = get_event_index(event);
-    
-    // event must have handler
-    ASSERT(obj->handlers[index])
-    
-    // event must not be enabled
-    ASSERT(!(obj->waitEvents&event))
-    
-    // update events
-    obj->waitEvents |= event;
-    
-    // update bottom
-    if (!obj->dispatching) {
-        update_bottom(obj);
-    }
-}
-
-void BPRFileDesc_DisableEvent (BPRFileDesc *obj, PRInt16 event)
-{
-    DebugObject_Access(&obj->d_obj);
-    
-    // get index
-    int index = get_event_index(event);
-    
-    // event must have handler
-    ASSERT(obj->handlers[index])
-    
-    // event must be enabled
-    ASSERT(obj->waitEvents&event)
-    
-    // update events
-    obj->waitEvents &= ~event;
-    obj->ready_events &= ~event;
-    
-    // update bottom
-    if (!obj->dispatching) {
-        update_bottom(obj);
-    }
-}
-
-BReactor * BPRFileDesc_Reactor (BPRFileDesc *obj)
-{
-    DebugObject_Access(&obj->d_obj);
-    
-    return BSocket_Reactor((BSocket *)obj->bottom->secret);
-}

+ 0 - 143
nspr_support/BPRFileDesc.h

@@ -1,143 +0,0 @@
-/**
- * @file BPRFileDesc.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * Object used for obtaining notifications for available I/O operations
- * on NSPR file descriptors (PRFileDesc) with supported bottom layers.
- * Currently only the {@link BSocketPRFileDesc} bottom layer is supported.
- */
-
-#ifndef BADVPN_NSPRSUPPORT_BPRFILEDESC_H
-#define BADVPN_NSPRSUPPORT_BPRFILEDESC_H
-
-#include <base/BPending.h>
-#include <base/DebugObject.h>
-#include <nspr_support/BSocketPRFileDesc.h>
-
-#define BPRFILEDESC_BOTTOM_BSOCKET 1
-
-/**
- * Handler function called when an event occurs on the NSPR file descriptor.
- * It is guaranteed that the event had a handler and was enabled.
- * The event is disabled before the handler is called.
- * 
- * It is guaranteed that the handler returns control to the reactor immediately.
- * 
- * @param user as in {@link BPRFileDesc_AddEventHandler}
- * @param event event being reported
- */
-typedef void (*BPRFileDesc_handler) (void *user, PRInt16 event);
-
-/**
- * Object used for obtaining notifications for available I/O operations
- * on NSPR file descriptors (PRFileDesc) with supported bottom layers.
- * Currently only the {@link BSocketPRFileDesc} bottom layer is supported.
- */
-typedef struct {
-    DebugObject d_obj;
-    BReactor *reactor;
-    PRFileDesc *prfd;
-    BPRFileDesc_handler handlers[2];
-    void *handlers_user[2];
-    PRInt16 waitEvents;
-    
-    // bottom
-    int bottom_type;
-    PRFileDesc *bottom;
-    
-    // event dispatching
-    int dispatching;
-    PRInt16 ready_events;
-    int current_event_index;
-    BPending job;
-} BPRFileDesc;
-
-/**
- * Initializes the object.
- * 
- * @param obj the object
- * @param prfd NSPR file descriptor for which notifications are needed.
- *             Its bottom layer must be a {@link BSocketPRFileDesc}.
- *             The bottom {@link BSocket} must not have any event handlers
- *             registered (socket-global or event-specific).
- *             This object registers a socket-global event handler for
- *             the bottom {@link BSocket}.
- */
-void BPRFileDesc_Init (BPRFileDesc *obj, PRFileDesc *prfd);
-
-/**
- * Frees the object.
- * @param obj the object
- */
-void BPRFileDesc_Free (BPRFileDesc *obj);
-
-/**
- * Registers a handler for an event.
- * The event must not already have a handler.
- * 
- * @param obj the object
- * @param event NSPR event to register the handler for. Must be
- *              PR_POLL_READ or PR_POLL_WRITE.
- * @param user value to pass to handler
- */
-void BPRFileDesc_AddEventHandler (BPRFileDesc *obj, PRInt16 event, BPRFileDesc_handler handler, void *user);
-
-/**
- * Unregisters a handler for an event.
- * The event must have a handler.
- * 
- * @param obj the object
- * @param event NSPR event to unregister the handler for
- */
-void BPRFileDesc_RemoveEventHandler (BPRFileDesc *obj, PRInt16 event);
-
-/**
- * Enables monitoring of an event.
- * The event must have a handler.
- * The event must not be enabled.
- * If the operation associated with the event can already be performed,
- * the handler for the event may never be called.
- * 
- * @param obj the object
- * @param event NSPR event to enable monitoring for
- */
-void BPRFileDesc_EnableEvent (BPRFileDesc *obj, PRInt16 event);
-
-/**
- * Disables monitoring of an event.
- * The event must have a handler.
- * The event must be enabled.
- * 
- * @param obj the object
- * @param event NSPR event to disable monitoring for
- */
-void BPRFileDesc_DisableEvent (BPRFileDesc *obj, PRInt16 event);
-
-/**
- * Returns the {@link BReactor} used by this object.
- * 
- * @param obj the object
- * @return {@link BReactor} used by this object
- */
-BReactor * BPRFileDesc_Reactor (BPRFileDesc *obj);
-
-#endif

+ 658 - 0
nspr_support/BSSLConnection.c

@@ -0,0 +1,658 @@
+/**
+ * @file BSSLConnection.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 <prerror.h>
+#include <ssl.h>
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <base/BLog.h>
+
+#include "BSSLConnection.h"
+
+#include <generated/blog_channel_BSSLConnection.h>
+
+static void connection_init_job_handler (BSSLConnection *o);
+static void connection_init_up (BSSLConnection *o);
+static void connection_try_io (BSSLConnection *o);
+static void connection_recv_job_handler (BSSLConnection *o);
+static void connection_try_send (BSSLConnection *o);
+static void connection_try_recv (BSSLConnection *o);
+static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len);
+static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len);
+
+int bprconnection_initialized = 0;
+PRDescIdentity bprconnection_identity;
+
+static PRFileDesc * get_bottom (PRFileDesc *layer)
+{
+    while (layer->lower) {
+        layer = layer->lower;
+    }
+    
+    return layer;
+}
+
+static PRStatus method_close (PRFileDesc *fd)
+{
+    struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret;
+    ASSERT(!b->con)
+    
+    // free backend
+    free(b);
+    
+    // set no secret
+    fd->secret = NULL;
+    
+    return PR_SUCCESS;
+}
+
+static PRInt32 method_read (PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+    struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret;
+    ASSERT(amount > 0)
+    
+    // if we are receiving into buffer or buffer has no data left, refuse recv
+    if (b->recv_busy || b->recv_pos == b->recv_len) {
+        // start receiving if not already
+        if (!b->recv_busy) {
+            // set recv busy
+            b->recv_busy = 1;
+            
+            // receive into buffer
+            StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE);
+        }
+        
+        PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+        return -1;
+    }
+    
+    // limit amount to available data
+    if (amount > b->recv_len - b->recv_pos) {
+        amount = b->recv_len - b->recv_pos;
+    }
+    
+    // copy data
+    memcpy(buf, b->recv_buf + b->recv_pos, amount);
+    
+    // update buffer
+    b->recv_pos += amount;
+    
+    return amount;
+}
+
+static PRInt32 method_write (PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+    struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret;
+    ASSERT(amount > 0)
+    
+    // if there is data in buffer, refuse send
+    if (b->send_pos < b->send_len) {
+        PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+        return -1;
+    }
+    
+    // limit amount to buffer size
+    if (amount > BSSLCONNECTION_BUF_SIZE) {
+        amount = BSSLCONNECTION_BUF_SIZE;
+    }
+    
+    // init buffer
+    memcpy(b->send_buf, buf, amount);
+    b->send_pos = 0;
+    b->send_len = amount;
+    
+    // start sending
+    StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos);
+    
+    return amount;
+}
+
+static PRStatus method_shutdown (PRFileDesc *fd, PRIntn how)
+{
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return PR_FAILURE;
+}
+
+static PRInt32 method_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
+{
+    ASSERT(flags == 0)
+    
+    return method_read(fd, buf, amount);
+}
+
+static PRInt32 method_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
+{
+    ASSERT(flags == 0)
+    
+    return method_write(fd, buf, amount);
+}
+
+static PRInt16 method_poll (PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+    *out_flags = 0;
+    return in_flags;
+}
+
+static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr)
+{
+    memset(addr, 0, sizeof(*addr));
+    addr->raw.family = PR_AF_INET;
+    return PR_SUCCESS;
+}
+
+static PRStatus method_getsocketoption (PRFileDesc *fd, PRSocketOptionData *data)
+{
+    switch (data->option) {
+        case PR_SockOpt_Nonblocking:
+            data->value.non_blocking = PR_TRUE;
+            return PR_SUCCESS;
+    }
+    
+    PR_SetError(PR_UNKNOWN_ERROR, 0);
+    return PR_FAILURE;
+}
+
+static PRStatus method_setsocketoption (PRFileDesc *fd, const PRSocketOptionData *data)
+{
+    PR_SetError(PR_UNKNOWN_ERROR, 0);
+    return PR_FAILURE;
+}
+
+static PRIntn _PR_InvalidIntn (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return -1;
+}
+
+static PRInt32 _PR_InvalidInt32 (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return -1;
+}
+
+static PRInt64 _PR_InvalidInt64 (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return -1;
+}
+
+static PROffset32 _PR_InvalidOffset32 (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return -1;
+}
+
+static PROffset64 _PR_InvalidOffset64 (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return -1;
+}
+
+static PRStatus _PR_InvalidStatus (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return PR_FAILURE;
+}
+
+static PRFileDesc *_PR_InvalidDesc (void)
+{
+    ASSERT(0)
+    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+    return NULL;
+}
+
+static PRIOMethods methods = {
+    (PRDescType)0,
+    method_close,
+    method_read,
+    method_write,
+    (PRAvailableFN)_PR_InvalidInt32,
+    (PRAvailable64FN)_PR_InvalidInt64,
+    (PRFsyncFN)_PR_InvalidStatus,
+    (PRSeekFN)_PR_InvalidOffset32,
+    (PRSeek64FN)_PR_InvalidOffset64,
+    (PRFileInfoFN)_PR_InvalidStatus,
+    (PRFileInfo64FN)_PR_InvalidStatus,
+    (PRWritevFN)_PR_InvalidInt32,
+    (PRConnectFN)_PR_InvalidStatus,
+    (PRAcceptFN)_PR_InvalidDesc,
+    (PRBindFN)_PR_InvalidStatus,
+    (PRListenFN)_PR_InvalidStatus,
+    method_shutdown,
+    method_recv,
+    method_send,
+    (PRRecvfromFN)_PR_InvalidInt32,
+    (PRSendtoFN)_PR_InvalidInt32,
+    method_poll,
+    (PRAcceptreadFN)_PR_InvalidInt32,
+    (PRTransmitfileFN)_PR_InvalidInt32,
+    (PRGetsocknameFN)_PR_InvalidStatus,
+    method_getpeername,
+    (PRReservedFN)_PR_InvalidIntn,
+    (PRReservedFN)_PR_InvalidIntn,
+    method_getsocketoption,
+    method_setsocketoption,
+    (PRSendfileFN)_PR_InvalidInt32,
+    (PRConnectcontinueFN)_PR_InvalidStatus,
+    (PRReservedFN)_PR_InvalidIntn,
+    (PRReservedFN)_PR_InvalidIntn,
+    (PRReservedFN)_PR_InvalidIntn,
+    (PRReservedFN)_PR_InvalidIntn
+};
+
+static void backend_send_if_handler_done (struct BSSLConnection_backend *b, int data_len)
+{
+    ASSERT(b->send_len > 0)
+    ASSERT(b->send_pos < b->send_len)
+    ASSERT(data_len > 0)
+    ASSERT(data_len <= b->send_len - b->send_pos)
+    
+    // update buffer
+    b->send_pos += data_len;
+    
+    // send more if needed
+    if (b->send_pos < b->send_len) {
+        StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos);
+        return;
+    }
+    
+    // notify connection
+    if (b->con && !b->con->have_error) {
+        connection_try_io(b->con);
+        return;
+    }
+}
+
+static void backend_recv_if_handler_done (struct BSSLConnection_backend *b, int data_len)
+{
+    ASSERT(b->recv_busy)
+    ASSERT(data_len > 0)
+    ASSERT(data_len <= BSSLCONNECTION_BUF_SIZE)
+    
+    // init buffer
+    b->recv_busy = 0;
+    b->recv_pos = 0;
+    b->recv_len = data_len;
+    
+    // notify connection
+    if (b->con && !b->con->have_error) {
+        connection_try_io(b->con);
+        return;
+    }
+}
+
+static void connection_report_error (BSSLConnection *o)
+{
+    ASSERT(!o->have_error)
+    
+    // set error
+    o->have_error = 1;
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BSSLCONNECTION_EVENT_ERROR));
+}
+
+static void connection_init_job_handler (BSSLConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->have_error)
+    ASSERT(!o->up)
+    
+    connection_try_io(o);
+    return;
+}
+
+static void connection_init_up (BSSLConnection *o)
+{
+    // init send interface
+    StreamPassInterface_Init(&o->send_if, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, BReactor_PendingGroup(o->reactor));
+    
+    // init recv interface
+    StreamRecvInterface_Init(&o->recv_if, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor));
+    
+    // init recv job
+    BPending_Init(&o->recv_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_recv_job_handler, o);
+    
+    // set no send data
+    o->send_len = -1;
+    
+    // set no recv data
+    o->recv_avail = -1;
+    
+    // set up
+    o->up = 1;
+}
+
+static void connection_try_io (BSSLConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->have_error)
+    
+    if (!o->up) {
+        // unset init job (in case backend called us before it executed)
+        BPending_Unset(&o->init_job);
+        
+        // try handshake
+        SECStatus res = SSL_ForceHandshake(o->prfd);
+        if (res == SECFailure) {
+            PRErrorCode error = PR_GetError();
+            if (error == PR_WOULD_BLOCK_ERROR) {
+                return;
+            }
+            
+            BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", error);
+            
+            connection_report_error(o);
+            return;
+        }
+        
+        // init up
+        connection_init_up(o);
+        
+        // report up
+        o->handler(o->user, BSSLCONNECTION_EVENT_UP);
+        return;
+    }
+    
+    if (o->send_len > 0) {
+        if (o->recv_avail > 0) {
+            BPending_Set(&o->recv_job);
+        }
+        
+        connection_try_send(o);
+        return;
+    }
+    
+    if (o->recv_avail > 0) {
+        connection_try_recv(o);
+        return;
+    }
+}
+
+static void connection_recv_job_handler (BSSLConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->have_error)
+    ASSERT(o->up)
+    ASSERT(o->recv_avail > 0)
+    
+    connection_try_recv(o);
+    return;
+}
+
+static void connection_try_send (BSSLConnection *o)
+{
+    ASSERT(!o->have_error)
+    ASSERT(o->up)
+    ASSERT(o->send_len > 0)
+    
+    // send
+    PRInt32 res = PR_Write(o->prfd, o->send_data, o->send_len);
+    if (res < 0) {
+        PRErrorCode error = PR_GetError();
+        if (error == PR_WOULD_BLOCK_ERROR) {
+            return;
+        }
+        
+        BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error);
+        
+        connection_report_error(o);
+        return;
+    }
+    
+    ASSERT(res > 0)
+    ASSERT(res <= o->send_len)
+    
+    // set no send data
+    o->send_len = -1;
+    
+    // done
+    StreamPassInterface_Done(&o->send_if, res);
+}
+
+static void connection_try_recv (BSSLConnection *o)
+{
+    ASSERT(!o->have_error)
+    ASSERT(o->up)
+    ASSERT(o->recv_avail > 0)
+    
+    // unset recv job
+    BPending_Unset(&o->recv_job);
+    
+    // recv
+    PRInt32 res = PR_Read(o->prfd, o->recv_data, o->recv_avail);
+    if (res < 0) {
+        PRErrorCode error = PR_GetError();
+        if (error == PR_WOULD_BLOCK_ERROR) {
+            return;
+        }
+        
+        BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error);
+        
+        connection_report_error(o);
+        return;
+    }
+    
+    if (res == 0) {
+        BLog(BLOG_ERROR, "PR_Read returned 0");
+        
+        connection_report_error(o);
+        return;
+    }
+    
+    ASSERT(res > 0)
+    ASSERT(res <= o->recv_avail)
+    
+    // set no recv data
+    o->recv_avail = -1;
+    
+    // done
+    StreamRecvInterface_Done(&o->recv_if, res);
+}
+
+static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->have_error)
+    ASSERT(o->up)
+    ASSERT(o->send_len == -1)
+    ASSERT(data_len > 0)
+    
+    // limit amount for PR_Write
+    if (data_len > INT32_MAX) {
+        data_len = INT32_MAX;
+    }
+    
+    // set send data
+    o->send_data = data;
+    o->send_len = data_len;
+    
+    // start sending
+    connection_try_send(o);
+    return;
+}
+
+static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->have_error)
+    ASSERT(o->up)
+    ASSERT(o->recv_avail == -1)
+    ASSERT(data_len > 0)
+    
+    // limit amount for PR_Read
+    if (data_len > INT32_MAX) {
+        data_len = INT32_MAX;
+    }
+    
+    // set recv data
+    o->recv_data = data;
+    o->recv_avail = data_len;
+    
+    // start receiving
+    connection_try_recv(o);
+    return;
+}
+
+int BSSLConnection_GlobalInit (void)
+{
+    ASSERT(!bprconnection_initialized)
+    
+    if ((bprconnection_identity = PR_GetUniqueIdentity("BSSLConnection")) == PR_INVALID_IO_LAYER) {
+        BLog(BLOG_ERROR, "PR_GetUniqueIdentity failed");
+        return 0;
+    }
+    
+    bprconnection_initialized = 1;
+    
+    return 1;
+}
+
+int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if)
+{
+    ASSERT(bprconnection_initialized)
+    
+    // allocate backend
+    struct BSSLConnection_backend *b = malloc(sizeof(*b));
+    if (!b) {
+        BLog(BLOG_ERROR, "malloc failed");
+        return 0;
+    }
+    
+    // init arguments
+    b->send_if = send_if;
+    b->recv_if = recv_if;
+    
+    // init interfaces
+    StreamPassInterface_Sender_Init(b->send_if, (StreamPassInterface_handler_done)backend_send_if_handler_done, b);
+    StreamRecvInterface_Receiver_Init(b->recv_if, (StreamRecvInterface_handler_done)backend_recv_if_handler_done, b);
+    
+    // set no connection
+    b->con = NULL;
+    
+    // init send buffer
+    b->send_len = 0;
+    b->send_pos = 0;
+    
+    // init recv buffer
+    b->recv_busy = 0;
+    b->recv_pos = 0;
+    b->recv_len = 0;
+    
+    // init prfd
+    memset(prfd, 0, sizeof(*prfd));
+    prfd->methods = &methods;
+    prfd->secret = (PRFilePrivate *)b;
+    prfd->identity = bprconnection_identity;
+    
+    return 1;
+}
+
+void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BReactor *reactor, void *user,
+                          BSSLConnection_handler handler)
+{
+    ASSERT(force_handshake == 0 || force_handshake == 1)
+    ASSERT(handler)
+    ASSERT(bprconnection_initialized)
+    ASSERT(get_bottom(prfd)->identity == bprconnection_identity)
+    ASSERT(!((struct BSSLConnection_backend *)(get_bottom(prfd)->secret))->con)
+    
+    // init arguments
+    o->prfd = prfd;
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // set backend
+    o->backend = (struct BSSLConnection_backend *)(get_bottom(prfd)->secret);
+    
+    // set have no error
+    o->have_error = 0;
+    
+    // init init job
+    BPending_Init(&o->init_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_init_job_handler, o);
+    
+    if (force_handshake) {
+        // set not up
+        o->up = 0;
+        
+        // set init job
+        BPending_Set(&o->init_job);
+    } else {
+        // init up
+        connection_init_up(o);
+    }
+    
+    // set backend connection
+    o->backend->con = o;
+    
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
+    DebugObject_Init(&o->d_obj);
+}
+
+void BSSLConnection_Free (BSSLConnection *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
+    
+    if (o->up) {
+        // free recv job
+        BPending_Free(&o->recv_job);
+        
+        // free recv interface
+        StreamRecvInterface_Free(&o->recv_if);
+        
+        // free send interface
+        StreamPassInterface_Free(&o->send_if);
+    }
+    
+    // free init job
+    BPending_Free(&o->init_job);
+    
+    // unset backend connection
+    o->backend->con = NULL;
+}
+
+StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->up)
+    
+    return &o->send_if;
+}
+
+StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->up)
+    
+    return &o->recv_if;
+}

+ 86 - 0
nspr_support/BSSLConnection.h

@@ -0,0 +1,86 @@
+/**
+ * @file BSSLConnection.h
+ * @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.
+ */
+
+#ifndef BADVPN_BSSLCONNECTION_H
+#define BADVPN_BSSLCONNECTION_H
+
+#include <prio.h>
+
+#include <misc/debug.h>
+#include <misc/debugerror.h>
+#include <base/DebugObject.h>
+#include <system/BReactor.h>
+#include <flow/StreamPassInterface.h>
+#include <flow/StreamRecvInterface.h>
+
+#define BSSLCONNECTION_EVENT_UP 1
+#define BSSLCONNECTION_EVENT_ERROR 2
+
+#define BSSLCONNECTION_BUF_SIZE 4096
+
+typedef void (*BSSLConnection_handler) (void *user, int event);
+
+struct BSSLConnection_backend;
+
+typedef struct {
+    PRFileDesc *prfd;
+    BReactor *reactor;
+    void *user;
+    BSSLConnection_handler handler;
+    struct BSSLConnection_backend *backend;
+    int have_error;
+    int up;
+    BPending init_job;
+    StreamPassInterface send_if;
+    StreamRecvInterface recv_if;
+    BPending recv_job;
+    const uint8_t *send_data;
+    int send_len;
+    uint8_t *recv_data;
+    int recv_avail;
+    DebugError d_err;
+    DebugObject d_obj;
+} BSSLConnection;
+
+struct BSSLConnection_backend {
+    StreamPassInterface *send_if;
+    StreamRecvInterface *recv_if;
+    BSSLConnection *con;
+    uint8_t send_buf[BSSLCONNECTION_BUF_SIZE];
+    int send_pos;
+    int send_len;
+    uint8_t recv_buf[BSSLCONNECTION_BUF_SIZE];
+    int recv_busy;
+    int recv_pos;
+    int recv_len;
+};
+
+int BSSLConnection_GlobalInit (void) WARN_UNUSED;
+int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if) WARN_UNUSED;
+
+void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BReactor *reactor, void *user,
+                          BSSLConnection_handler handler);
+void BSSLConnection_Free (BSSLConnection *o);
+StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o);
+StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o);
+
+#endif

+ 0 - 297
nspr_support/BSocketPRFileDesc.c

@@ -1,297 +0,0 @@
-/**
- * @file BSocketPRFileDesc.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 <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include <prerror.h>
-#include <prmem.h>
-
-#include <misc/debug.h>
-#include <misc/offset.h>
-#include <base/BLog.h>
-
-#include <nspr_support/BSocketPRFileDesc.h>
-
-#include <generated/blog_channel_BSocketPRFileDesc.h>
-
-#ifndef NDEBUG
-int bsocketprfiledesc_initialized = 0;
-#endif
-PRDescIdentity bsocketprfiledesc_identity;
-
-static int baddr_to_prnetaddr (PRNetAddr *out, BAddr addr)
-{
-    memset(out, 0, sizeof(PRNetAddr));
-    
-    switch (addr.type) {
-        case BADDR_TYPE_IPV4:
-            out->inet.family = PR_AF_INET;
-            out->inet.port = addr.ipv4.port;
-            out->inet.ip = addr.ipv4.ip;
-            break;
-        case BADDR_TYPE_IPV6:
-            out->ipv6.family = PR_AF_INET6;
-            out->ipv6.port = addr.ipv6.port;
-            out->ipv6.flowinfo = 0;
-            memcpy(&out->ipv6.ip, addr.ipv6.ip, 16);
-            break;
-        default:
-            return 0;
-    }
-    
-    return 1;
-}
-
-static PRStatus method_close (PRFileDesc *fd)
-{
-    return PR_SUCCESS;
-}
-
-static PRInt32 method_read (PRFileDesc *fd, void *buf, PRInt32 amount)
-{
-    ASSERT(amount >= 0)
-    
-    BSocket *bsock = (BSocket *)fd->secret;
-    
-    if (amount > INT_MAX) {
-        amount = INT_MAX;
-    }
-    
-    int res = BSocket_Recv(bsock, buf, amount);
-    if (res < 0) {
-        switch (BSocket_GetError(bsock)) {
-            case BSOCKET_ERROR_LATER:
-                PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
-                return -1;
-            default:
-                BLog(BLOG_NOTICE, "BSocket_Recv failed (%d)", BSocket_GetError(bsock));
-                PR_SetError(PR_UNKNOWN_ERROR, 0);
-                return -1;
-        }
-    }
-    
-    return res;
-}
-
-static PRInt32 method_write (PRFileDesc *fd, const void *buf, PRInt32 amount)
-{
-    ASSERT(amount >= 0)
-    
-    BSocket *bsock = (BSocket *)fd->secret;
-    
-    if (amount > INT_MAX) {
-        amount = INT_MAX;
-    }
-    
-    int res = BSocket_Send(bsock, (uint8_t *)buf, amount);
-    ASSERT(res != 0)
-    if (res < 0) {
-        switch (BSocket_GetError(bsock)) {
-            case BSOCKET_ERROR_LATER:
-                PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
-                return -1;
-            default:
-                BLog(BLOG_NOTICE, "BSocket_Send failed (%d)", BSocket_GetError(bsock));
-                PR_SetError(PR_UNKNOWN_ERROR, 0);
-                return -1;
-        }
-    }
-    
-    return res;
-}
-
-static PRStatus method_shutdown (PRFileDesc *fd, PRIntn how)
-{
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return PR_FAILURE;
-}
-
-static PRInt32 method_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
-{
-    ASSERT(flags == 0)
-    
-    return method_read(fd, buf, amount);
-}
-
-static PRInt32 method_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
-{
-    ASSERT(flags == 0)
-    
-    return method_write(fd, buf, amount);
-}
-
-static PRInt16 method_poll (PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
-{
-    *out_flags = 0;
-    return in_flags;
-}
-
-static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr)
-{
-    BSocket *bsock = (BSocket *)fd->secret;
-    
-    BAddr baddr;
-    if (BSocket_GetPeerName(bsock, &baddr) < 0) {
-        PR_SetError(PR_UNKNOWN_ERROR, 0);
-        return PR_FAILURE;
-    }
-    
-    if (!baddr_to_prnetaddr(addr, baddr)) {
-        PR_SetError(PR_UNKNOWN_ERROR, 0);
-        return PR_FAILURE;
-    }
-    
-    return PR_SUCCESS;
-}
-
-static PRStatus method_getsocketoption (PRFileDesc *fd, PRSocketOptionData *data)
-{
-    switch (data->option) {
-        case PR_SockOpt_Nonblocking:
-            data->value.non_blocking = PR_TRUE;
-            return PR_SUCCESS;
-    }
-    
-    PR_SetError(PR_UNKNOWN_ERROR, 0);
-    return PR_FAILURE;
-}
-
-static PRStatus method_setsocketoption (PRFileDesc *fd, const PRSocketOptionData *data)
-{
-    PR_SetError(PR_UNKNOWN_ERROR, 0);
-    return PR_FAILURE;
-}
-
-static PRIntn _PR_InvalidIntn (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return -1;
-}
-
-static PRInt32 _PR_InvalidInt32 (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return -1;
-}
-
-static PRInt64 _PR_InvalidInt64 (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return -1;
-}
-
-static PROffset32 _PR_InvalidOffset32 (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return -1;
-}
-
-static PROffset64 _PR_InvalidOffset64 (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return -1;
-}
-
-static PRStatus _PR_InvalidStatus (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return PR_FAILURE;
-}
-
-static PRFileDesc *_PR_InvalidDesc (void)
-{
-    ASSERT(0)
-    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
-    return NULL;
-}
-
-static PRIOMethods methods = {
-    (PRDescType)0,
-    method_close,
-    method_read,
-    method_write,
-    (PRAvailableFN)_PR_InvalidInt32,
-    (PRAvailable64FN)_PR_InvalidInt64,
-    (PRFsyncFN)_PR_InvalidStatus,
-    (PRSeekFN)_PR_InvalidOffset32,
-    (PRSeek64FN)_PR_InvalidOffset64,
-    (PRFileInfoFN)_PR_InvalidStatus,
-    (PRFileInfo64FN)_PR_InvalidStatus,
-    (PRWritevFN)_PR_InvalidInt32,
-    (PRConnectFN)_PR_InvalidStatus,
-    (PRAcceptFN)_PR_InvalidDesc,
-    (PRBindFN)_PR_InvalidStatus,
-    (PRListenFN)_PR_InvalidStatus,
-    method_shutdown,
-    method_recv,
-    method_send,
-    (PRRecvfromFN)_PR_InvalidInt32,
-    (PRSendtoFN)_PR_InvalidInt32,
-    method_poll,
-    (PRAcceptreadFN)_PR_InvalidInt32,
-    (PRTransmitfileFN)_PR_InvalidInt32,
-    (PRGetsocknameFN)_PR_InvalidStatus,
-    method_getpeername,
-    (PRReservedFN)_PR_InvalidIntn,
-    (PRReservedFN)_PR_InvalidIntn,
-    method_getsocketoption,
-    method_setsocketoption,
-    (PRSendfileFN)_PR_InvalidInt32,
-    (PRConnectcontinueFN)_PR_InvalidStatus,
-    (PRReservedFN)_PR_InvalidIntn,
-    (PRReservedFN)_PR_InvalidIntn,
-    (PRReservedFN)_PR_InvalidIntn,
-    (PRReservedFN)_PR_InvalidIntn
-};
-
-int BSocketPRFileDesc_GlobalInit (void)
-{
-    ASSERT(!bsocketprfiledesc_initialized)
-    
-    if ((bsocketprfiledesc_identity = PR_GetUniqueIdentity("BSocketPRFileDesc")) == PR_INVALID_IO_LAYER) {
-        return 0;
-    }
-    
-    #ifndef NDEBUG
-    bsocketprfiledesc_initialized = 1;
-    #endif
-    
-    return 1;
-}
-
-void BSocketPRFileDesc_Create (PRFileDesc *prfd, BSocket *bsock)
-{
-    ASSERT(bsocketprfiledesc_initialized)
-    
-    memset(prfd, 0, sizeof(prfd));
-    prfd->methods = &methods;
-    prfd->secret = (PRFilePrivate *)bsock;
-    prfd->identity = bsocketprfiledesc_identity;
-}

+ 0 - 54
nspr_support/BSocketPRFileDesc.h

@@ -1,54 +0,0 @@
-/**
- * @file BSocketPRFileDesc.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * NSPR file descriptor (PRFileDesc) for {@link BSocket} stream sockets.
- */
-
-#ifndef BADVPN_NSPRSUPPORT_BSOCKETPRFILEDESC_H
-#define BADVPN_NSPRSUPPORT_BSOCKETPRFILEDESC_H
-
-#include <prio.h>
-
-#include <misc/debug.h>
-#include <system/BSocket.h>
-
-extern PRDescIdentity bsocketprfiledesc_identity;
-
-/**
- * Globally initializes the {@link BSocket} NSPR file descriptor backend.
- * Must not have been called successfully.
- *
- * @return 1 on success, 0 on failure
- */
-int BSocketPRFileDesc_GlobalInit (void) WARN_UNUSED;
-
-/**
- * Creates a NSPR file descriptor using {@link BSocket} for I/O.
- * {@link BSocketPRFileDesc_GlobalInit} must have been done.
- *
- * @param prfd uninitialized PRFileDesc structure
- * @param bsock socket to use. The socket should be a stream socket.
- */
-void BSocketPRFileDesc_Create (PRFileDesc *prfd, BSocket *bsock);
-
-#endif

+ 2 - 5
nspr_support/CMakeLists.txt

@@ -1,8 +1,5 @@
 add_library(nspr_support
-    BSocketPRFileDesc.c
     DummyPRFileDesc.c
-    BPRFileDesc.c
-    PRStreamSource.c
-    PRStreamSink.c
+    BSSLConnection.c
 )
-target_link_libraries(nspr_support system ${NSPR_LIBRARIES})
+target_link_libraries(nspr_support system flow ${NSPR_LIBRARIES} ${NSS_LIBRARIES})

+ 0 - 124
nspr_support/PRStreamSink.c

@@ -1,124 +0,0 @@
-/**
- * @file PRStreamSink.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 <inttypes.h>
-
-#include <prerror.h>
-
-#include <misc/debug.h>
-#include <base/BLog.h>
-
-#include <nspr_support/PRStreamSink.h>
-
-#include <generated/blog_channel_PRStreamSink.h>
-
-static void report_error (PRStreamSink *s, int error)
-{
-    DEBUGERROR(&s->d_err, FlowErrorReporter_ReportError(&s->rep, error))
-}
-
-static void try_send (PRStreamSink *s)
-{
-    ASSERT(s->in_len > 0)
-    
-    int res = PR_Write(s->bprfd->prfd, s->in, s->in_len);
-    if (res < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR) {
-        // wait for socket in prfd_handler
-        BPRFileDesc_EnableEvent(s->bprfd, PR_POLL_WRITE);
-        return;
-    }
-    
-    if (res < 0) {
-        BLog(BLOG_NOTICE, "PR_Write failed (%"PRIi32")", PR_GetError());
-        report_error(s, PRSTREAMSINK_ERROR_NSPR);
-        return;
-    }
-    
-    ASSERT(res > 0)
-    ASSERT(res <= s->in_len)
-    
-    // finish packet
-    s->in_len = -1;
-    StreamPassInterface_Done(&s->input, res);
-}
-
-static void input_handler_send (PRStreamSink *s, uint8_t *data, int data_len)
-{
-    ASSERT(data_len > 0)
-    ASSERT(s->in_len == -1)
-    DebugObject_Access(&s->d_obj);
-    
-    // set packet
-    s->in_len = data_len;
-    s->in = data;
-    
-    try_send(s);
-    return;
-}
-
-static void prfd_handler (PRStreamSink *s, PRInt16 event)
-{
-    ASSERT(s->in_len > 0)
-    ASSERT(event == PR_POLL_WRITE)
-    DebugObject_Access(&s->d_obj);
-    
-    try_send(s);
-    return;
-}
-
-void PRStreamSink_Init (PRStreamSink *s, FlowErrorReporter rep, BPRFileDesc *bprfd, BPendingGroup *pg)
-{
-    // init arguments
-    s->rep = rep;
-    s->bprfd = bprfd;
-    
-    // add socket event handler
-    BPRFileDesc_AddEventHandler(s->bprfd, PR_POLL_WRITE, (BPRFileDesc_handler)prfd_handler, s);
-    
-    // init input
-    StreamPassInterface_Init(&s->input, (StreamPassInterface_handler_send)input_handler_send, s, pg);
-    
-    // have no input packet
-    s->in_len = -1;
-    
-    DebugObject_Init(&s->d_obj);
-    DebugError_Init(&s->d_err, BReactor_PendingGroup(BPRFileDesc_Reactor(s->bprfd)));
-}
-
-void PRStreamSink_Free (PRStreamSink *s)
-{
-    DebugError_Free(&s->d_err);
-    DebugObject_Free(&s->d_obj);
-    
-    // free input
-    StreamPassInterface_Free(&s->input);
-    
-    // remove socket event handler
-    BPRFileDesc_RemoveEventHandler(s->bprfd, PR_POLL_WRITE);
-}
-
-StreamPassInterface * PRStreamSink_GetInput (PRStreamSink *s)
-{
-    DebugObject_Access(&s->d_obj);
-    
-    return &s->input;
-}

+ 0 - 82
nspr_support/PRStreamSink.h

@@ -1,82 +0,0 @@
-/**
- * @file PRStreamSink.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A {@link StreamPassInterface} sink for a NSPR file descriptor (PRFileDesc) via {@link BPRFileDesc}.
- */
-
-#ifndef BADVPN_NSPRSUPPORT_PRSTREAMSINK_H
-#define BADVPN_NSPRSUPPORT_PRSTREAMSINK_H
-
-#include <stdint.h>
-
-#include <misc/debugerror.h>
-#include <base/DebugObject.h>
-#include <flow/StreamPassInterface.h>
-#include <flow/FlowError.h>
-#include <nspr_support/BPRFileDesc.h>
-
-#define PRSTREAMSINK_ERROR_NSPR 1
-
-/**
- * A {@link StreamPassInterface} sink for a NSPR file descriptor (PRFileDesc) via {@link BPRFileDesc}.
- */
-typedef struct {
-    FlowErrorReporter rep;
-    BPRFileDesc *bprfd;
-    StreamPassInterface input;
-    int in_len;
-    uint8_t *in;
-    DebugObject d_obj;
-    DebugError d_err;
-} PRStreamSink;
-
-/**
- * Initializes the object.
- *
- * @param s the object
- * @param rep error reporting data. Error code is an int. Possible error codes:
- *              - PRSTREAMSINK_ERROR_NSPR: {@link PR_Write} failed
- *                with an unhandled error code
- *            The object must be freed from the error handler.
- * @param bprfd the {@link BPRFileDesc} object to write data to. Registers a
- *              PR_POLL_WRITE handler which must not be registered.
- * @param pg pending group
- */
-void PRStreamSink_Init (PRStreamSink *s, FlowErrorReporter rep, BPRFileDesc *bprfd, BPendingGroup *pg);
-
-/**
- * Frees the object.
- *
- * @param s the object
- */
-void PRStreamSink_Free (PRStreamSink *s);
-
-/**
- * Returns the input interface.
- *
- * @param s the object
- * @return input interface
- */
-StreamPassInterface * PRStreamSink_GetInput (PRStreamSink *s);
-
-#endif

+ 0 - 130
nspr_support/PRStreamSource.c

@@ -1,130 +0,0 @@
-/**
- * @file PRStreamSource.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 <inttypes.h>
-
-#include <prerror.h>
-
-#include <misc/debug.h>
-#include <base/BLog.h>
-
-#include <nspr_support/PRStreamSource.h>
-
-#include <generated/blog_channel_PRStreamSource.h>
-
-static void report_error (PRStreamSource *s, int error)
-{
-    DEBUGERROR(&s->d_err, FlowErrorReporter_ReportError(&s->rep, error))
-}
-
-static void try_recv (PRStreamSource *s)
-{
-    ASSERT(s->out_avail > 0)
-    
-    int res = PR_Read(s->bprfd->prfd, s->out, s->out_avail);
-    if (res < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR) {
-        // wait for socket in prfd_handler
-        BPRFileDesc_EnableEvent(s->bprfd, PR_POLL_READ);
-        return;
-    }
-    
-    if (res < 0) {
-        BLog(BLOG_NOTICE, "PR_Read failed (%"PRIi32")", PR_GetError());
-        report_error(s, PRSTREAMSOURCE_ERROR_NSPR);
-        return;
-    }
-    
-    if (res == 0) {
-        BLog(BLOG_NOTICE, "Connection closed");
-        report_error(s, PRSTREAMSOURCE_ERROR_CLOSED);
-        return;
-    }
-    
-    ASSERT(res > 0)
-    ASSERT(res <= s->out_avail)
-    
-    // finish packet
-    s->out_avail = -1;
-    StreamRecvInterface_Done(&s->output, res);
-}
-
-static void output_handler_recv (PRStreamSource *s, uint8_t *data, int data_avail)
-{
-    ASSERT(data_avail > 0)
-    ASSERT(s->out_avail == -1)
-    DebugObject_Access(&s->d_obj);
-    
-    // set packet
-    s->out_avail = data_avail;
-    s->out = data;
-    
-    try_recv(s);
-    return;
-}
-
-static void prfd_handler (PRStreamSource *s, PRInt16 event)
-{
-    ASSERT(s->out_avail > 0)
-    ASSERT(event == PR_POLL_READ)
-    DebugObject_Access(&s->d_obj);
-    
-    try_recv(s);
-    return;
-}
-
-void PRStreamSource_Init (PRStreamSource *s, FlowErrorReporter rep, BPRFileDesc *bprfd, BPendingGroup *pg)
-{
-    // init arguments
-    s->rep = rep;
-    s->bprfd = bprfd;
-    
-    // add socket event handler
-    BPRFileDesc_AddEventHandler(s->bprfd, PR_POLL_READ, (BPRFileDesc_handler)prfd_handler, s);
-    
-    // init output
-    StreamRecvInterface_Init(&s->output, (StreamRecvInterface_handler_recv)output_handler_recv, s, pg);
-    
-    // have no output packet
-    s->out_avail = -1;
-    
-    DebugObject_Init(&s->d_obj);
-    DebugError_Init(&s->d_err, BReactor_PendingGroup(BPRFileDesc_Reactor(s->bprfd)));
-}
-
-void PRStreamSource_Free (PRStreamSource *s)
-{
-    DebugError_Free(&s->d_err);
-    DebugObject_Free(&s->d_obj);
-    
-    // free output
-    StreamRecvInterface_Free(&s->output);
-    
-    // remove socket event handler
-    BPRFileDesc_RemoveEventHandler(s->bprfd, PR_POLL_READ);
-}
-
-StreamRecvInterface * PRStreamSource_GetOutput (PRStreamSource *s)
-{
-    DebugObject_Access(&s->d_obj);
-    
-    return &s->output;
-}

+ 0 - 84
nspr_support/PRStreamSource.h

@@ -1,84 +0,0 @@
-/**
- * @file PRStreamSource.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A {@link StreamRecvInterface} source for a NSPR file descriptor (PRFileDesc) via {@link BPRFileDesc}.
- */
-
-#ifndef BADVPN_NSPRSUPPORT_PRSTREAMSOURCE_H
-#define BADVPN_NSPRSUPPORT_PRSTREAMSOURCE_H
-
-#include <stdint.h>
-
-#include <misc/debugerror.h>
-#include <base/DebugObject.h>
-#include <flow/StreamRecvInterface.h>
-#include <flow/FlowError.h>
-#include <nspr_support/BPRFileDesc.h>
-
-#define PRSTREAMSOURCE_ERROR_CLOSED 0
-#define PRSTREAMSOURCE_ERROR_NSPR 1
-
-/**
- * A {@link StreamRecvInterface} source for a NSPR file descriptor (PRFileDesc) via {@link BPRFileDesc}.
- */
-typedef struct {
-    FlowErrorReporter rep;
-    BPRFileDesc *bprfd;
-    StreamRecvInterface output;
-    int out_avail;
-    uint8_t *out;
-    DebugObject d_obj;
-    DebugError d_err;
-} PRStreamSource;
-
-/**
- * Initializes the object.
- *
- * @param s the object
- * @param rep error reporting data. Error code is an int. Possible error codes:
- *              - PRSTREAMSOURCE_ERROR_CLOSED: {@link PR_Read} returned 0
- *              - PRSTREAMSOURCE_ERROR_NSPR: {@link PR_Read} failed
- *                with an unhandled error code
- *            The object must be freed from the error handler.
- * @param bprfd the {@link BPRFileDesc} object to read data from. Registers a
- *              PR_POLL_READ handler which must not be registered.
- * @param pg pending group
- */
-void PRStreamSource_Init (PRStreamSource *s, FlowErrorReporter rep, BPRFileDesc *bprfd, BPendingGroup *pg);
-
-/**
- * Frees the object.
- *
- * @param s the object
- */
-void PRStreamSource_Free (PRStreamSource *s);
-
-/**
- * Returns the output interface.
- *
- * @param s the object
- * @return output interface
- */
-StreamRecvInterface * PRStreamSource_GetOutput (PRStreamSource *s);
-
-#endif

+ 76 - 122
server/server.c

@@ -50,8 +50,7 @@
 #include <base/BLog.h>
 #include <system/BSignal.h>
 #include <system/BTime.h>
-#include <system/BAddr.h>
-#include <system/Listener.h>
+#include <system/BNetwork.h>
 #include <security/BRandom.h>
 #include <nspr_support/DummyPRFileDesc.h>
 
@@ -63,10 +62,6 @@
 
 #include <generated/blog_channel_server.h>
 
-#define COMPONENT_SOURCE 1
-#define COMPONENT_SINK 2
-#define COMPONENT_DECODER 3
-
 #define LOGGER_STDOUT 1
 #define LOGGER_SYSLOG 2
 
@@ -138,7 +133,7 @@ PRFileDesc model_dprfd;
 PRFileDesc *model_prfd;
 
 // listeners
-Listener listeners[MAX_LISTEN_ADDRS];
+BListener listeners[MAX_LISTEN_ADDRS];
 int num_listeners;
 
 // number of connected clients
@@ -169,7 +164,7 @@ static int process_arguments (void);
 static void signal_handler (void *unused);
 
 // listener handler, accepts new clients
-static void listener_handler (Listener *listener);
+static void listener_handler (BListener *listener);
 
 // frees resources used by a client
 static void client_dealloc (struct client_data *client);
@@ -192,14 +187,14 @@ static void client_log (struct client_data *client, int level, const char *fmt,
 // client activity timer handler. Removes the client.
 static void client_disconnect_timer_handler (struct client_data *client);
 
-// drives cline SSL handshake
-static void client_try_handshake (struct client_data *client);
+// BConnection handler
+static void client_connection_handler (struct client_data *client, int event);
 
-// event handler for driving client SSL handshake
-static void client_handshake_read_handler (struct client_data *client, PRInt16 event);
+// BSSLConnection handler
+static void client_sslcon_handler (struct client_data *client, int event);
 
-// handler for client I/O errors. Removes the client.
-static void client_error_handler (struct client_data *client, int component, int code);
+// decoder handler
+static void client_decoder_handler (struct client_data *client, int component, int code);
 
 // provides a buffer for sending a control packet to the client
 static int client_start_control_packet (struct client_data *client, void **data, int len);
@@ -339,9 +334,9 @@ int main (int argc, char *argv[])
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
-    // initialize sockets
-    if (BSocket_GlobalInit() < 0) {
-        BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
+    // initialize network
+    if (!BNetwork_GlobalInit()) {
+        BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
         goto fail1;
     }
     
@@ -405,8 +400,8 @@ int main (int argc, char *argv[])
             BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed");
             goto fail3;
         }
-        if (!BSocketPRFileDesc_GlobalInit()) {
-            BLog(BLOG_ERROR, "BSocketPRFileDesc_GlobalInit failed");
+        if (!BSSLConnection_GlobalInit()) {
+            BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed");
             goto fail3;
         }
         
@@ -462,8 +457,8 @@ int main (int argc, char *argv[])
     // initialize listeners
     num_listeners = 0;
     while (num_listeners < num_listen_addrs) {
-        if (!Listener_Init(&listeners[num_listeners], &ss, listen_addrs[num_listeners], (Listener_handler)listener_handler, &listeners[num_listeners])) {
-            BLog(BLOG_ERROR, "Listener_Init failed");
+        if (!BListener_Init(&listeners[num_listeners], listen_addrs[num_listeners], &ss, &listeners[num_listeners], (BListener_handler)listener_handler)) {
+            BLog(BLOG_ERROR, "BListener_Init failed");
             goto fail7;
         }
         num_listeners++;
@@ -511,7 +506,7 @@ int main (int argc, char *argv[])
 fail7:
     while (num_listeners > 0) {
         num_listeners--;
-        Listener_Free(&listeners[num_listeners]);
+        BListener_Free(&listeners[num_listeners]);
     }
     
     if (options.ssl) {
@@ -776,7 +771,7 @@ void signal_handler (void *unused)
     BReactor_Quit(&ss, 0);
 }
 
-void listener_handler (Listener *listener)
+void listener_handler (BListener *listener)
 {
     if (clients_num == MAX_CLIENTS) {
         BLog(BLOG_WARNING, "too many clients for new client");
@@ -791,14 +786,14 @@ void listener_handler (Listener *listener)
     }
     
     // accept connection
-    if (!Listener_Accept(listener, &client->sock, &client->addr)) {
-        BLog(BLOG_NOTICE, "Listener_Accept failed");
+    if (!BConnection_Init(&client->con, BCONNECTION_SOURCE_LISTENER(listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
         goto fail1;
     }
     
     // limit socket send buffer, else our scheduling is pointless
-    if (BSocket_SetSendBuffer(&client->sock, CLIENT_SOCKET_SEND_BUFFER) < 0) {
-        BLog(BLOG_WARNING, "BSocket_SetSendBuffer failed");
+    if (!BConnection_SetSendBuffer(&client->con, CLIENT_SOCKET_SEND_BUFFER) < 0) {
+        BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed");
     }
     
     // assign ID
@@ -809,11 +804,18 @@ void listener_handler (Listener *listener)
     
     // now client_log() works
     
+    // init connection interfaces
+    BConnection_SendAsync_Init(&client->con);
+    BConnection_RecvAsync_Init(&client->con);
+    
     if (options.ssl) {
-        // create BSocket NSPR file descriptor
-        BSocketPRFileDesc_Create(&client->bottom_prfd, &client->sock);
+        // create bottom NSPR file descriptor
+        if (!BSSLConnection_MakeBackend(&client->bottom_prfd, BConnection_SendAsync_GetIf(&client->con), BConnection_RecvAsync_GetIf(&client->con))) {
+            client_log(client, BLOG_ERROR, "BSSLConnection_MakeBackend failed");
+            goto fail2;
+        }
         
-        // create SSL file descriptor from the socket's BSocketPRFileDesc
+        // create SSL file descriptor from the bottom NSPR file descriptor
         if (!(client->ssl_prfd = SSL_ImportFD(model_prfd, &client->bottom_prfd))) {
             client_log(client, BLOG_ERROR, "SSL_ImportFD failed");
             ASSERT_FORCE(PR_Close(&client->bottom_prfd) == PR_SUCCESS)
@@ -836,8 +838,8 @@ void listener_handler (Listener *listener)
             goto fail3;
         }
         
-        // initialize BPRFileDesc on SSL file descriptor
-        BPRFileDesc_Init(&client->ssl_bprfd, client->ssl_prfd);
+        // init SSL connection
+        BSSLConnection_Init(&client->sslcon, client->ssl_prfd, 1, &ss, client, (BSSLConnection_handler)client_sslcon_handler);
     } else {
         // initialize I/O
         if (!client_init_io(client)) {
@@ -871,24 +873,16 @@ void listener_handler (Listener *listener)
     
     client_log(client, BLOG_INFO, "initialized");
     
-    // start I/O
-    if (options.ssl) {
-        // set read handler for driving handshake
-        BPRFileDesc_AddEventHandler(&client->ssl_bprfd, PR_POLL_READ, (BPRFileDesc_handler)client_handshake_read_handler, client);
-        
-        // start handshake
-        client_try_handshake(client);
-        return;
-    } else {
-        return;
-    }
+    return;
     
     if (options.ssl) {
 fail3:
         ASSERT_FORCE(PR_Close(client->ssl_prfd) == PR_SUCCESS)
     }
 fail2:
-    BSocket_Free(&client->sock);
+    BConnection_RecvAsync_Free(&client->con);
+    BConnection_SendAsync_Free(&client->con);
+    BConnection_Free(&client->con);
 fail1:
     free(client);
 fail0:
@@ -919,7 +913,7 @@ void client_dealloc (struct client_data *client)
     
     // free SSL
     if (options.ssl) {
-        BPRFileDesc_Free(&client->ssl_bprfd);
+        BSSLConnection_Free(&client->sslcon);
         ASSERT_FORCE(PR_Close(client->ssl_prfd) == PR_SUCCESS)
     }
     
@@ -928,8 +922,12 @@ void client_dealloc (struct client_data *client)
         PORT_Free(client->common_name);
     }
     
-    // free socket
-    BSocket_Free(&client->sock);
+    // free connection interfaces
+    BConnection_RecvAsync_Free(&client->con);
+    BConnection_SendAsync_Free(&client->con);
+    
+    // free connection
+    BConnection_Free(&client->con);
     
     // free memory
     free(client);
@@ -937,27 +935,18 @@ void client_dealloc (struct client_data *client)
 
 int client_init_io (struct client_data *client)
 {
-    // initialize error domain
-    FlowErrorDomain_Init(&client->domain, (FlowErrorDomain_handler)client_error_handler, client);
+    StreamPassInterface *send_if = (options.ssl ? BSSLConnection_GetSendIf(&client->sslcon) : BConnection_SendAsync_GetIf(&client->con));
+    StreamRecvInterface *recv_if = (options.ssl ? BSSLConnection_GetRecvIf(&client->sslcon) : BConnection_RecvAsync_GetIf(&client->con));
     
     // init input
     
-    // init source
-    StreamRecvInterface *source_interface;
-    if (options.ssl) {
-        PRStreamSource_Init(&client->input_source.ssl, FlowErrorReporter_Create(&client->domain, COMPONENT_SOURCE), &client->ssl_bprfd, BReactor_PendingGroup(&ss));
-        source_interface = PRStreamSource_GetOutput(&client->input_source.ssl);
-    } else {
-        StreamSocketSource_Init(&client->input_source.plain, FlowErrorReporter_Create(&client->domain, COMPONENT_SOURCE), &client->sock, BReactor_PendingGroup(&ss));
-        source_interface = StreamSocketSource_GetOutput(&client->input_source.plain);
-    }
-    
     // init interface
     PacketPassInterface_Init(&client->input_interface, SC_MAX_ENC, (PacketPassInterface_handler_send)client_input_handler_send, client, BReactor_PendingGroup(&ss));
     
     // init decoder
-    if (!PacketProtoDecoder_Init(&client->input_decoder, FlowErrorReporter_Create(&client->domain, COMPONENT_DECODER),
-        source_interface, &client->input_interface, BReactor_PendingGroup(&ss)
+    FlowErrorDomain_Init(&client->input_decoder_domain, (FlowErrorDomain_handler)client_decoder_handler, client);
+    if (!PacketProtoDecoder_Init(&client->input_decoder, FlowErrorReporter_Create(&client->input_decoder_domain, 0),
+        recv_if, &client->input_interface, BReactor_PendingGroup(&ss)
     )) {
         client_log(client, BLOG_ERROR, "PacketProtoDecoder_Init failed");
         goto fail1;
@@ -965,18 +954,8 @@ int client_init_io (struct client_data *client)
     
     // init output common
     
-    // init sink
-    StreamPassInterface *sink_interface;
-    if (options.ssl) {
-        PRStreamSink_Init(&client->output_sink.ssl, FlowErrorReporter_Create(&client->domain, COMPONENT_SINK), &client->ssl_bprfd, BReactor_PendingGroup(&ss));
-        sink_interface = PRStreamSink_GetInput(&client->output_sink.ssl);
-    } else {
-        StreamSocketSink_Init(&client->output_sink.plain, FlowErrorReporter_Create(&client->domain, COMPONENT_SINK), &client->sock, BReactor_PendingGroup(&ss));
-        sink_interface = StreamSocketSink_GetInput(&client->output_sink.plain);
-    }
-    
     // init sender
-    PacketStreamSender_Init(&client->output_sender, sink_interface, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(&ss));
+    PacketStreamSender_Init(&client->output_sender, send_if, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(&ss));
     
     // init queue
     PacketPassPriorityQueue_Init(&client->output_priorityqueue, PacketStreamSender_GetInput(&client->output_sender), BReactor_PendingGroup(&ss), 0);
@@ -1016,20 +995,10 @@ fail2:
     // free output common
     PacketPassPriorityQueue_Free(&client->output_priorityqueue);
     PacketStreamSender_Free(&client->output_sender);
-    if (options.ssl) {
-        PRStreamSink_Free(&client->output_sink.ssl);
-    } else {
-        StreamSocketSink_Free(&client->output_sink.plain);
-    }
     // free input
     PacketProtoDecoder_Free(&client->input_decoder);
 fail1:
     PacketPassInterface_Free(&client->input_interface);
-    if (options.ssl) {
-        PRStreamSource_Free(&client->input_source.ssl);
-    } else {
-        StreamSocketSource_Free(&client->input_source.plain);
-    }
     return 0;
 }
 
@@ -1060,20 +1029,10 @@ void client_dealloc_io (struct client_data *client)
     // free output common
     PacketPassPriorityQueue_Free(&client->output_priorityqueue);
     PacketStreamSender_Free(&client->output_sender);
-    if (options.ssl) {
-        PRStreamSink_Free(&client->output_sink.ssl);
-    } else {
-        StreamSocketSink_Free(&client->output_sink.plain);
-    }
     
     // free input
     PacketProtoDecoder_Free(&client->input_decoder);
     PacketPassInterface_Free(&client->input_interface);
-    if (options.ssl) {
-        PRStreamSource_Free(&client->input_source.ssl);
-    } else {
-        StreamSocketSource_Free(&client->input_source.plain);
-    }
 }
 
 void client_remove (struct client_data *client)
@@ -1160,25 +1119,34 @@ void client_disconnect_timer_handler (struct client_data *client)
     return;
 }
 
-void client_try_handshake (struct client_data *client)
+void client_connection_handler (struct client_data *client, int event)
 {
-    ASSERT(client->initstatus == INITSTATUS_HANDSHAKE)
     ASSERT(!client->dying)
     
-    // attempt handshake
-    if (SSL_ForceHandshake(client->ssl_prfd) != SECSuccess) {
-        PRErrorCode error = PR_GetError();
-        if (error == PR_WOULD_BLOCK_ERROR) {
-            // try again on read event
-            BPRFileDesc_EnableEvent(&client->ssl_bprfd, PR_POLL_READ);
-            return;
-        }
-        client_log(client, BLOG_NOTICE, "SSL_ForceHandshake failed (%d)", (int)error);
-        goto fail0;
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
+        client_log(client, BLOG_INFO, "connection closed");
+    } else {
+        client_log(client, BLOG_INFO, "connection error");
+    }
+    
+    client_remove(client);
+    return;
+}
+
+void client_sslcon_handler (struct client_data *client, int event)
+{
+    ASSERT(options.ssl)
+    ASSERT(!client->dying)
+    ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR)
+    ASSERT(!(event == BSSLCONNECTION_EVENT_UP) || client->initstatus == INITSTATUS_HANDSHAKE)
+    
+    if (event == BSSLCONNECTION_EVENT_ERROR) {
+        client_log(client, BLOG_ERROR, "SSL error");
+        client_remove(client);
+        return;
     }
     
-    // remove read handler
-    BPRFileDesc_RemoveEventHandler(&client->ssl_bprfd, PR_POLL_READ);
+    client_log(client, BLOG_INFO, "handshake complete");
     
     // get client certificate
     CERTCertificate *cert = SSL_PeerCertificate(client->ssl_prfd);
@@ -1247,26 +1215,12 @@ fail0:
     client_remove(client);
 }
 
-void client_handshake_read_handler (struct client_data *client, PRInt16 event)
-{
-    ASSERT(client->initstatus == INITSTATUS_HANDSHAKE)
-    ASSERT(!client->dying)
-    ASSERT(event == PR_POLL_READ)
-    
-    // restart no data timer
-    BReactor_SetTimer(&ss, &client->disconnect_timer);
-    
-    // continue handshake
-    client_try_handshake(client);
-    return;
-}
-
-void client_error_handler (struct client_data *client, int component, int code)
+void client_decoder_handler (struct client_data *client, int component, int code)
 {
     ASSERT(INITSTATUS_HASLINK(client->initstatus))
     ASSERT(!client->dying)
     
-    client_log(client, BLOG_NOTICE, "error");
+    client_log(client, BLOG_ERROR, "decoder error");
     
     client_remove(client);
     return;

+ 6 - 20
server/server.h

@@ -25,17 +25,13 @@
 #include <protocol/scproto.h>
 #include <structure/LinkedList2.h>
 #include <structure/BAVL.h>
-#include <system/BSocket.h>
 #include <flow/PacketProtoDecoder.h>
 #include <flow/PacketStreamSender.h>
 #include <flow/PacketPassPriorityQueue.h>
 #include <flow/PacketPassFairQueue.h>
 #include <flow/PacketProtoFlow.h>
-#include <flowextra/StreamSocketSource.h>
-#include <flowextra/StreamSocketSink.h>
-#include <nspr_support/BPRFileDesc.h>
-#include <nspr_support/PRStreamSource.h>
-#include <nspr_support/PRStreamSink.h>
+#include <system/BConnection.h>
+#include <nspr_support/BSSLConnection.h>
 
 // name of the program
 #define PROGRAM_NAME "server"
@@ -101,13 +97,13 @@ struct peer_know {
 
 struct client_data {
     // socket
-    BSocket sock;
+    BConnection con;
     BAddr addr;
     
-    // SSL file descriptor
+    // SSL connection, if using SSL
     PRFileDesc bottom_prfd;
     PRFileDesc *ssl_prfd;
-    BPRFileDesc ssl_bprfd;
+    BSSLConnection sslcon;
     
     // initialization state
     int initstatus;
@@ -145,22 +141,12 @@ struct client_data {
     int dying;
     BPending dying_job;
     
-    // error domain
-    FlowErrorDomain domain;
-    
     // input
-    union {
-        StreamSocketSource plain;
-        PRStreamSource ssl;
-    } input_source;
+    FlowErrorDomain input_decoder_domain;
     PacketProtoDecoder input_decoder;
     PacketPassInterface input_interface;
     
     // output common
-    union {
-        StreamSocketSink plain;
-        PRStreamSink ssl;
-    } output_sink;
     PacketStreamSender output_sender;
     PacketPassPriorityQueue output_priorityqueue;
     

+ 85 - 90
server_connection/ServerConnection.c

@@ -33,15 +33,13 @@
 #define STATE_WAITINIT 2
 #define STATE_COMPLETE 3
 
-#define COMPONENT_SOURCE 1
-#define COMPONENT_SINK 2
-#define COMPONENT_DECODER 3
-
 static void report_error (ServerConnection *o);
-static void connect_handler (ServerConnection *o, int event);
+static void connector_handler (ServerConnection *o, int is_error);
 static void pending_handler (ServerConnection *o);
 static SECStatus client_auth_data_callback (ServerConnection *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey);
-static void error_handler (ServerConnection *o, int component, int code);
+static void connection_handler (ServerConnection *o, int event);
+static void sslcon_handler (ServerConnection *o, int event);
+static void decoder_handler (ServerConnection *o, int component, int code);
 static void input_handler_send (ServerConnection *o, uint8_t *data, int data_len);
 static void packet_hello (ServerConnection *o, uint8_t *data, int data_len);
 static void packet_newclient (ServerConnection *o, uint8_t *data, int data_len);
@@ -55,33 +53,44 @@ void report_error (ServerConnection *o)
     DEBUGERROR(&o->d_err, o->handler_error(o->user))
 }
 
-void connect_handler (ServerConnection *o, int event)
+void connector_handler (ServerConnection *o, int is_error)
 {
-    ASSERT(o->state == STATE_CONNECTING)
-    ASSERT(event == BSOCKET_CONNECT)
     DebugObject_Access(&o->d_obj);
-    
-    // remove connect event handler
-    BSocket_RemoveEventHandler(&o->sock, BSOCKET_CONNECT);
+    ASSERT(o->state == STATE_CONNECTING)
     
     // check connection attempt result
-    int res = BSocket_GetConnectResult(&o->sock);
-    if (res != 0) {
-        BLog(BLOG_ERROR, "connection failed (BSocket error %d)", res);
+    if (is_error) {
+        BLog(BLOG_ERROR, "connection failed");
         goto fail0;
     }
     
     BLog(BLOG_NOTICE, "connected");
     
+    // init connection
+    if (!BConnection_Init(&o->con, BCONNECTION_SOURCE_CONNECTOR(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
+        goto fail0;
+    }
+    
+    // init connection interfaces
+    BConnection_SendAsync_Init(&o->con);
+    BConnection_RecvAsync_Init(&o->con);
+    
+    StreamPassInterface *send_iface = BConnection_SendAsync_GetIf(&o->con);
+    StreamRecvInterface *recv_iface = BConnection_RecvAsync_GetIf(&o->con);
+    
     if (o->have_ssl) {
-        // create BSocket NSPR file descriptor
-        BSocketPRFileDesc_Create(&o->bottom_prfd, &o->sock);
+        // create bottom NSPR file descriptor
+        if (!BSSLConnection_MakeBackend(&o->bottom_prfd, send_iface, recv_iface)) {
+            BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed");
+            goto fail0a;
+        }
         
-        // create SSL file descriptor from the socket's BSocketPRFileDesc
+        // create SSL file descriptor from the bottom NSPR file descriptor
         if (!(o->ssl_prfd = SSL_ImportFD(NULL, &o->bottom_prfd))) {
             BLog(BLOG_ERROR, "SSL_ImportFD failed");
             ASSERT_FORCE(PR_Close(&o->bottom_prfd) == PR_SUCCESS)
-            goto fail0;
+            goto fail0a;
         }
         
         // set client mode
@@ -102,24 +111,17 @@ void connect_handler (ServerConnection *o, int event)
             goto fail1;
         }
         
-        // init BPRFileDesc
-        BPRFileDesc_Init(&o->ssl_bprfd, o->ssl_prfd);
+        // init BSSLConnection
+        BSSLConnection_Init(&o->sslcon, o->ssl_prfd, 0, o->reactor, o, (BSSLConnection_handler)sslcon_handler);
+        
+        send_iface = BSSLConnection_GetSendIf(&o->sslcon);
+        recv_iface = BSSLConnection_GetRecvIf(&o->sslcon);
     }
     
-    // init error domain
-    FlowErrorDomain_Init(&o->ioerrdomain, (FlowErrorDomain_handler)error_handler, o);
-    
     // init input chain
-    StreamRecvInterface *source_interface;
-    if (o->have_ssl) {
-        PRStreamSource_Init(&o->input_source.ssl, FlowErrorReporter_Create(&o->ioerrdomain, COMPONENT_SOURCE), &o->ssl_bprfd, BReactor_PendingGroup(o->reactor));
-        source_interface = PRStreamSource_GetOutput(&o->input_source.ssl);
-    } else {
-        StreamSocketSource_Init(&o->input_source.plain, FlowErrorReporter_Create(&o->ioerrdomain, COMPONENT_SOURCE), &o->sock, BReactor_PendingGroup(o->reactor));
-        source_interface = StreamSocketSource_GetOutput(&o->input_source.plain);
-    }
     PacketPassInterface_Init(&o->input_interface, SC_MAX_ENC, (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(o->reactor));
-    if (!PacketProtoDecoder_Init(&o->input_decoder, FlowErrorReporter_Create(&o->ioerrdomain, COMPONENT_DECODER), source_interface, &o->input_interface,BReactor_PendingGroup(o->reactor))) {
+    FlowErrorDomain_Init(&o->input_decoder_domain, (FlowErrorDomain_handler)decoder_handler, o);
+    if (!PacketProtoDecoder_Init(&o->input_decoder, FlowErrorReporter_Create(&o->input_decoder_domain, 0), recv_iface, &o->input_interface, BReactor_PendingGroup(o->reactor))) {
         BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed");
         goto fail2;
     }
@@ -136,18 +138,8 @@ void connect_handler (ServerConnection *o, int event)
     
     // init output common
     
-    // init sink
-    StreamPassInterface *sink_interface;
-    if (o->have_ssl) {
-        PRStreamSink_Init(&o->output_sink.ssl, FlowErrorReporter_Create(&o->ioerrdomain, COMPONENT_SINK), &o->ssl_bprfd, BReactor_PendingGroup(o->reactor));
-        sink_interface = PRStreamSink_GetInput(&o->output_sink.ssl);
-    } else {
-        StreamSocketSink_Init(&o->output_sink.plain, FlowErrorReporter_Create(&o->ioerrdomain, COMPONENT_SINK), &o->sock, BReactor_PendingGroup(o->reactor));
-        sink_interface = StreamSocketSink_GetInput(&o->output_sink.plain);
-    }
-    
     // init sender
-    PacketStreamSender_Init(&o->output_sender, sink_interface, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(o->reactor));
+    PacketStreamSender_Init(&o->output_sender, send_iface, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(o->reactor));
     
     // init keepalives
     if (!KeepaliveIO_Init(&o->output_keepaliveio, o->reactor, PacketStreamSender_GetInput(&o->output_sender), PacketProtoEncoder_GetOutput(&o->output_ka_encoder), o->keepalive_interval)) {
@@ -183,36 +175,25 @@ void connect_handler (ServerConnection *o, int event)
     
 fail4:
     PacketPassPriorityQueueFlow_Free(&o->output_local_qflow);
-    // free output common
     PacketPassPriorityQueue_Free(&o->output_queue);
     KeepaliveIO_Free(&o->output_keepaliveio);
 fail3:
     PacketStreamSender_Free(&o->output_sender);
-    if (o->have_ssl) {
-        PRStreamSink_Free(&o->output_sink.ssl);
-    } else {
-        StreamSocketSink_Free(&o->output_sink.plain);
-    }
-    // free output keep-alive branch
     PacketProtoEncoder_Free(&o->output_ka_encoder);
     SCKeepaliveSource_Free(&o->output_ka_zero);
-    // free job
     BPending_Free(&o->start_job);
-    // free input
     PacketProtoDecoder_Free(&o->input_decoder);
 fail2:
     PacketPassInterface_Free(&o->input_interface);
     if (o->have_ssl) {
-        PRStreamSource_Free(&o->input_source.ssl);
-    } else {
-        StreamSocketSource_Free(&o->input_source.plain);
-    }
-    // free SSL
-    if (o->have_ssl) {
-        BPRFileDesc_Free(&o->ssl_bprfd);
+        BSSLConnection_Free(&o->sslcon);
 fail1:
         ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS)
     }
+fail0a:
+    BConnection_RecvAsync_Free(&o->con);
+    BConnection_SendAsync_Free(&o->con);
+    BConnection_Free(&o->con);
 fail0:
     // report error
     report_error(o);
@@ -255,10 +236,40 @@ SECStatus client_auth_data_callback (ServerConnection *o, PRFileDesc *fd, CERTDi
     return SECSuccess;
 }
 
-void error_handler (ServerConnection *o, int component, int code)
+void connection_handler (ServerConnection *o, int event)
 {
+    DebugObject_Access(&o->d_obj);
     ASSERT(o->state >= STATE_WAITINIT)
+    
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
+        BLog(BLOG_INFO, "connection closed");
+    } else {
+        BLog(BLOG_INFO, "connection error");
+    }
+    
+    report_error(o);
+    return;
+}
+
+void sslcon_handler (ServerConnection *o, int event)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->have_ssl)
+    ASSERT(o->state >= STATE_WAITINIT)
+    ASSERT(event == BSSLCONNECTION_EVENT_ERROR)
+    
+    BLog(BLOG_ERROR, "SSL error");
+    
+    report_error(o);
+    return;
+}
+
+void decoder_handler (ServerConnection *o, int component, int code)
+{
     DebugObject_Access(&o->d_obj);
+    ASSERT(o->state >= STATE_WAITINIT)
+    
+    BLog(BLOG_ERROR, "decoder error");
     
     report_error(o);
     return;
@@ -487,23 +498,12 @@ int ServerConnection_Init (
     o->handler_endclient = handler_endclient;
     o->handler_message = handler_message;
     
-    // init socket
-    if (BSocket_Init(&o->sock, o->reactor, addr.type, BSOCKET_TYPE_STREAM) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Init failed (%d)", BSocket_GetError(&o->sock));
+    // init connector
+    if (!BConnector_Init(&o->connector, addr, o->reactor, o, (BConnector_handler)connector_handler)) {
+        BLog(BLOG_ERROR, "BConnector_Init failed");
         goto fail0;
     }
     
-    // start connecting
-    int res = BSocket_Connect(&o->sock, &addr, 1);
-    if (res != -1 || BSocket_GetError(&o->sock) != BSOCKET_ERROR_IN_PROGRESS) {
-        BLog(BLOG_ERROR, "BSocket_Connect failed (%d)", BSocket_GetError(&o->sock));
-        goto fail1;
-    }
-    
-    // be informed of connection result
-    BSocket_AddEventHandler(&o->sock, BSOCKET_CONNECT, (BSocket_handler)connect_handler, o);
-    BSocket_EnableEvent(&o->sock, BSOCKET_CONNECT);
-    
     // set state
     o->state = STATE_CONNECTING;
     
@@ -511,8 +511,6 @@ int ServerConnection_Init (
     DebugObject_Init(&o->d_obj);
     return 1;
     
-fail1:
-    BSocket_Free(&o->sock);
 fail0:
     return 0;
 }
@@ -537,11 +535,6 @@ void ServerConnection_Free (ServerConnection *o)
         PacketPassPriorityQueue_Free(&o->output_queue);
         KeepaliveIO_Free(&o->output_keepaliveio);
         PacketStreamSender_Free(&o->output_sender);
-        if (o->have_ssl) {
-            PRStreamSink_Free(&o->output_sink.ssl);
-        } else {
-            StreamSocketSink_Free(&o->output_sink.plain);
-        }
         
         // free output keep-alive branch
         PacketProtoEncoder_Free(&o->output_ka_encoder);
@@ -553,21 +546,23 @@ void ServerConnection_Free (ServerConnection *o)
         // free input chain
         PacketProtoDecoder_Free(&o->input_decoder);
         PacketPassInterface_Free(&o->input_interface);
-        if (o->have_ssl) {
-            PRStreamSource_Free(&o->input_source.ssl);
-        } else {
-            StreamSocketSource_Free(&o->input_source.plain);
-        }
         
         // free SSL
         if (o->have_ssl) {
-            BPRFileDesc_Free(&o->ssl_bprfd);
+            BSSLConnection_Free(&o->sslcon);
             ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS)
         }
+        
+        // free connection interfaces
+        BConnection_RecvAsync_Free(&o->con);
+        BConnection_SendAsync_Free(&o->con);
+        
+        // free connection
+        BConnection_Free(&o->con);
     }
     
-    // free socket
-    BSocket_Free(&o->sock);
+    // free connector
+    BConnector_Free(&o->connector);
 }
 
 int ServerConnection_IsReady (ServerConnection *o)

+ 8 - 21
server_connection/ServerConnection.h

@@ -44,19 +44,15 @@
 #include <protocol/scproto.h>
 #include <protocol/msgproto.h>
 #include <base/DebugObject.h>
-#include <system/BSocket.h>
+#include <system/BConnection.h>
 #include <flow/FlowError.h>
 #include <flow/PacketProtoEncoder.h>
 #include <flow/PacketStreamSender.h>
 #include <flow/PacketProtoDecoder.h>
 #include <flow/PacketPassPriorityQueue.h>
 #include <flow/PacketProtoFlow.h>
-#include <flowextra/StreamSocketSink.h>
-#include <flowextra/StreamSocketSource.h>
 #include <flowextra/KeepaliveIO.h>
-#include <nspr_support/BPRFileDesc.h>
-#include <nspr_support/PRStreamSink.h>
-#include <nspr_support/PRStreamSource.h>
+#include <nspr_support/BSSLConnection.h>
 #include <server_connection/SCKeepaliveSource.h>
 
 /**
@@ -145,7 +141,8 @@ typedef struct {
     ServerConnection_handler_message handler_message;
     
     // socket
-    BSocket sock;
+    BConnector connector;
+    BConnection con;
     
     // state
     int state;
@@ -158,16 +155,10 @@ typedef struct {
     // SSL file descriptor, defined only if using SSL
     PRFileDesc bottom_prfd;
     PRFileDesc *ssl_prfd;
-    BPRFileDesc ssl_bprfd;
-    
-    // I/O error domain
-    FlowErrorDomain ioerrdomain;
+    BSSLConnection sslcon;
     
     // input
-    union {
-        StreamSocketSource plain;
-        PRStreamSource ssl;
-    } input_source;
+    FlowErrorDomain input_decoder_domain;
     PacketProtoDecoder input_decoder;
     PacketPassInterface input_interface;
     
@@ -179,10 +170,6 @@ typedef struct {
     PacketPassPriorityQueue output_queue;
     KeepaliveIO output_keepaliveio;
     PacketStreamSender output_sender;
-    union {
-        StreamSocketSink plain;
-        PRStreamSink ssl;
-    } output_sink;
     
     // output local flow
     int output_local_packet_len;
@@ -205,8 +192,8 @@ typedef struct {
  * Initializes the object.
  * The object is initialized in not ready state.
  * {@link BLog_Init} must have been done.
- * {@link BSocket_GlobalInit} must have been done.
- * {@link BSocketPRFileDesc_GlobalInit} must have been done if using SSL.
+ * {@link BNetwork_GlobalInit} must have been done.
+ * {@link BSSLConnection_GlobalInit} must have been done if using SSL.
  *
  * @param o the object
  * @param reactor {@link BReactor} we live in

+ 64 - 85
socksclient/BSocksClient.c

@@ -45,9 +45,8 @@ 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, int code);
-static void socket_error_handler (BSocksClient *o, int event);
-static void connect_handler (BSocksClient *o, int event);
+static void connector_handler (BSocksClient* o, int is_error);
+static void connection_handler (BSocksClient* o, int event);
 static void recv_handler_done (BSocksClient *o, int data_len);
 static void send_handler_done (BSocksClient *o);
 
@@ -59,13 +58,13 @@ void report_error (BSocksClient *o, int error)
 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);
+    BConnection_RecvAsync_Init(&o->con);
+    o->control.recv_if = BConnection_RecvAsync_GetIf(&o->con);
     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));
+    BConnection_SendAsync_Init(&o->con);
+    PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), 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);
 }
@@ -74,28 +73,28 @@ void free_control_io (BSocksClient *o)
 {
     // free sending
     PacketStreamSender_Free(&o->control.send_sender);
-    StreamSocketSink_Free(&o->control.send_sink);
+    BConnection_SendAsync_Free(&o->con);
     
     // free receiving
-    StreamSocketSource_Free(&o->control.recv_source);
+    BConnection_RecvAsync_Free(&o->con);
 }
 
 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));
+    BConnection_RecvAsync_Init(&o->con);
     
     // init sending
-    StreamSocketSink_Init(&o->up.send_sink, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), &o->sock, BReactor_PendingGroup(o->reactor));
+    BConnection_SendAsync_Init(&o->con);
 }
 
 void free_up_io (BSocksClient *o)
 {
     // free sending
-    StreamSocketSink_Free(&o->up.send_sink);
+    BConnection_SendAsync_Free(&o->con);
     
     // free receiving
-    StreamSocketSource_Free(&o->up.recv_source);
+    BConnection_RecvAsync_Free(&o->con);
 }
 
 void start_receive (BSocksClient *o, uint8_t *dest, int total)
@@ -116,46 +115,21 @@ void do_receive (BSocksClient *o)
     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, int code)
+void connector_handler (BSocksClient* o, int is_error)
 {
-    ASSERT(component == COMPONENT_SOURCE || component == COMPONENT_SINK)
     DebugObject_Access(&o->d_obj);
-    
-    if (o->state == STATE_UP && component == COMPONENT_SOURCE && code == STREAMSOCKETSOURCE_ERROR_CLOSED) {
-        report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED);
-        return;
-    }
-    
-    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 connection result
+    if (is_error) {
+        BLog(BLOG_ERROR, "connection failed");
+        goto fail0;
+    }
     
-    // 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;
+    // init connection
+    if (!BConnection_Init(&o->con, BCONNECTION_SOURCE_CONNECTOR(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
+        goto fail0;
     }
     
     BLog(BLOG_DEBUG, "connected");
@@ -171,6 +145,26 @@ void connect_handler (BSocksClient *o, int event)
     
     // set state
     o->state = STATE_SENDING_HELLO;
+    
+    return;
+    
+fail0:
+    report_error(o, BSOCKSCLIENT_EVENT_ERROR);
+    return;
+}
+
+void connection_handler (BSocksClient* o, int event)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->state != STATE_CONNECTING)
+    
+    if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) {
+        report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED);
+        return;
+    }
+    
+    report_error(o, BSOCKSCLIENT_EVENT_ERROR);
+    return;
 }
 
 void recv_handler_done (BSocksClient *o, int data_len)
@@ -328,29 +322,12 @@ int BSocksClient_Init (BSocksClient *o, BAddr server_addr, BAddr dest_addr, BSoc
     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");
+    // init connector
+    if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) {
+        BLog(BLOG_ERROR, "BConnector_Init failed");
         goto fail0;
     }
     
-    // connect socket
-    if (BSocket_Connect(&o->sock, &server_addr, 1) >= 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;
     
@@ -358,8 +335,6 @@ int BSocksClient_Init (BSocksClient *o, BAddr server_addr, BAddr dest_addr, BSoc
     DebugObject_Init(&o->d_obj);
     return 1;
     
-fail1:
-    BSocket_Free(&o->sock);
 fail0:
     return 0;
 }
@@ -369,21 +344,25 @@ void BSocksClient_Free (BSocksClient *o)
     DebugObject_Free(&o->d_obj);
     DebugError_Free(&o->d_err);
     
-    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);
+    if (o->state != STATE_CONNECTING) {
+        if (o->state == STATE_UP) {
+            // free up I/O
+            free_up_io(o);
+        } else {
+            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 connection
+        BConnection_Free(&o->con);
     }
     
-    // free socket
-    BSocket_Free(&o->sock);
+    // free connector
+    BConnector_Free(&o->connector);
 }
 
 StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o)
@@ -391,7 +370,7 @@ StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o)
     ASSERT(o->state == STATE_UP)
     DebugObject_Access(&o->d_obj);
     
-    return StreamSocketSink_GetInput(&o->up.send_sink);
+    return BConnection_SendAsync_GetIf(&o->con);
 }
 
 StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o)
@@ -399,5 +378,5 @@ StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o)
     ASSERT(o->state == STATE_UP)
     DebugObject_Access(&o->d_obj);
     
-    return StreamSocketSource_GetOutput(&o->up.recv_source);
+    return BConnection_RecvAsync_GetIf(&o->con);
 }

+ 3 - 11
socksclient/BSocksClient.h

@@ -33,10 +33,8 @@
 #include <misc/debugerror.h>
 #include <misc/socks_proto.h>
 #include <base/DebugObject.h>
-#include <system/BSocket.h>
+#include <system/BConnection.h>
 #include <flow/PacketStreamSender.h>
-#include <flowextra/StreamSocketSink.h>
-#include <flowextra/StreamSocketSource.h>
 
 #define BSOCKSCLIENT_EVENT_ERROR 1
 #define BSOCKSCLIENT_EVENT_UP 2
@@ -62,14 +60,12 @@ typedef struct {
     void *user;
     BReactor *reactor;
     int state;
-    FlowErrorDomain domain;
-    BSocket sock;
+    BConnector connector;
+    BConnection con;
     union {
         struct {
             PacketPassInterface *send_if;
             PacketStreamSender send_sender;
-            StreamSocketSink send_sink;
-            StreamSocketSource recv_source;
             StreamRecvInterface *recv_if;
             union {
                 struct {
@@ -96,10 +92,6 @@ typedef struct {
             int recv_len;
             int recv_total;
         } control;
-        struct {
-            StreamSocketSink send_sink;
-            StreamSocketSource recv_source;
-        } up;
     };
     DebugError d_err;
     DebugObject d_obj;

+ 0 - 4
system/BAddr.h

@@ -49,10 +49,6 @@
 #define BADDR_TYPE_NONE 0
 #define BADDR_TYPE_IPV4 1
 #define BADDR_TYPE_IPV6 2
-#ifndef BADVPN_USE_WINAPI
-    #define BADDR_TYPE_UNIX 3 // only a domain number for BSocket
-    #define BADDR_TYPE_UNIXPIPE 4 // only a domain number for BSocket
-#endif
 #ifdef BADVPN_LINUX
     #define BADDR_TYPE_PACKET 5
 #endif

+ 136 - 0
system/BConnection.h

@@ -0,0 +1,136 @@
+/**
+ * @file BConnection.h
+ * @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.
+ */
+
+#ifndef BADVPN_SYSTEM_BCONNECTION
+#define BADVPN_SYSTEM_BCONNECTION
+
+#include <misc/debug.h>
+#include <flow/StreamPassInterface.h>
+#include <flow/StreamRecvInterface.h>
+#include <system/BAddr.h>
+#include <system/BReactor.h>
+#include <system/BNetwork.h>
+
+
+
+int BConnection_AddressSupported (BAddr addr);
+
+
+
+struct BListener_s;
+typedef struct BListener_s BListener;
+
+typedef void (*BListener_handler) (void *user);
+
+int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user,
+                    BListener_handler handler) WARN_UNUSED;
+void BListener_Free (BListener *o);
+
+
+
+struct BConnector_s;
+typedef struct BConnector_s BConnector;
+
+typedef void (*BConnector_handler) (void *user, int is_error);
+
+int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user,
+                     BConnector_handler handler) WARN_UNUSED;
+void BConnector_Free (BConnector *o);
+
+
+
+#define BCONNECTION_SOURCE_TYPE_LISTENER 1
+#define BCONNECTION_SOURCE_TYPE_CONNECTOR 2
+#define BCONNECTION_SOURCE_TYPE_PIPE 3
+
+struct BConnection_source {
+    int type;
+    union {
+        struct {
+            BListener *listener;
+            BAddr *out_addr;
+        } listener;
+        struct {
+            BConnector *connector;
+        } connector;
+#ifndef BADVPN_USE_WINAPI
+        struct {
+            int pipefd;
+        } pipe;
+#endif
+    } u;
+};
+
+#define BCONNECTION_SOURCE_LISTENER(_listener, _out_addr) \
+    ((struct BConnection_source){ \
+        .type = BCONNECTION_SOURCE_TYPE_LISTENER, \
+        .u.listener.listener = (_listener), \
+        .u.listener.out_addr = (_out_addr) \
+    })
+
+#define BCONNECTION_SOURCE_CONNECTOR(_connector) \
+    ((struct BConnection_source){ \
+        .type = BCONNECTION_SOURCE_TYPE_CONNECTOR, \
+        .u.connector.connector = (_connector) \
+    })
+
+#ifndef BADVPN_USE_WINAPI
+#define BCONNECTION_SOURCE_PIPE(_pipefd) \
+    ((struct BConnection_source){ \
+        .type = BCONNECTION_SOURCE_TYPE_PIPE, \
+        .u.pipe.pipefd = (_pipefd) \
+    })
+#endif
+
+
+
+struct BConnection_s;
+typedef struct BConnection_s BConnection;
+
+#define BCONNECTION_EVENT_ERROR 1
+#define BCONNECTION_EVENT_RECVCLOSED 2
+
+typedef void (*BConnection_handler) (void *user, int event);
+
+int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user,
+                      BConnection_handler handler) WARN_UNUSED;
+void BConnection_Free (BConnection *o);
+void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler_event);
+int BConnection_SetSendBuffer (BConnection *o, int buf_size);
+
+void BConnection_SendAsync_Init (BConnection *o);
+void BConnection_SendAsync_Free (BConnection *o);
+StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o);
+
+void BConnection_RecvAsync_Init (BConnection *o);
+void BConnection_RecvAsync_Free (BConnection *o);
+StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o);
+
+
+
+#ifdef BADVPN_USE_WINAPI
+#include "BConnection_win.h"
+#else
+#include "BConnection_unix.h"
+#endif
+
+#endif

+ 793 - 0
system/BConnection_unix.c

@@ -0,0 +1,793 @@
+/**
+ * @file BConnection_unix.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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <misc/nonblocking.h>
+#include <base/BLog.h>
+
+#include "BConnection.h"
+
+#include <generated/blog_channel_BConnection.h>
+
+struct sys_addr {
+    socklen_t len;
+    union {
+        struct sockaddr generic;
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } addr;
+};
+
+static void addr_socket_to_sys (struct sys_addr *out, BAddr addr);
+static void addr_sys_to_socket (BAddr *out, struct sys_addr addr);
+static void listener_fd_handler (BListener *o, int events);
+static void listener_default_job_handler (BListener *o);
+static void connector_fd_handler (BConnector *o, int events);
+static void connector_job_handler (BConnector *o);
+static void connection_report_error (BConnection *o);
+static void connection_fd_handler (BConnection *o, int events);
+static void connection_send_job_handler (BConnection *o);
+static void connection_recv_job_handler (BConnection *o);
+static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len);
+static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_len);
+
+static void addr_socket_to_sys (struct sys_addr *out, BAddr addr)
+{
+    switch (addr.type) {
+        case BADDR_TYPE_IPV4: {
+            out->len = sizeof(out->addr.ipv4);
+            memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
+            out->addr.ipv4.sin_family = AF_INET;
+            out->addr.ipv4.sin_port = addr.ipv4.port;
+            out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip;
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            out->len = sizeof(out->addr.ipv6);
+            memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
+            out->addr.ipv6.sin6_family = AF_INET6;
+            out->addr.ipv6.sin6_port = addr.ipv6.port;
+            out->addr.ipv6.sin6_flowinfo = 0;
+            memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16);
+            out->addr.ipv6.sin6_scope_id = 0;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+static void addr_sys_to_socket (BAddr *out, struct sys_addr addr)
+{
+    switch (addr.addr.generic.sa_family) {
+        case AF_INET: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in))
+            BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port);
+        } break;
+        
+        case AF_INET6: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in6))
+            BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port);
+        } break;
+        
+        default: {
+            BAddr_InitNone(out);
+        } break;
+    }
+}
+
+static void listener_fd_handler (BListener *o, int events)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    // set default job
+    BPending_Set(&o->default_job);
+    
+    // call handler
+    o->handler(o->user);
+    return;
+}
+
+static void listener_default_job_handler (BListener *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    BLog(BLOG_ERROR, "discarding connection");
+    
+    // accept
+    int newfd = accept(o->fd, NULL, NULL);
+    if (newfd < 0) {
+        BLog(BLOG_ERROR, "accept failed");
+        return;
+    }
+    
+    // close new fd
+    if (close(newfd) < 0) {
+        BLog(BLOG_ERROR, "close failed");
+    }
+}
+
+static void connector_fd_handler (BConnector *o, int events)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->fd >= 0)
+    ASSERT(!o->connected)
+    ASSERT(o->have_bfd)
+    
+    // free BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    
+    // set have no BFileDescriptor
+    o->have_bfd = 0;
+    
+    // read connection result
+    int result;
+    socklen_t result_len = sizeof(result);
+    if (getsockopt(o->fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) {
+        BLog(BLOG_ERROR, "getsockopt failed");
+        goto fail0;
+    }
+    ASSERT_FORCE(result_len == sizeof(result))
+    
+    if (result != 0) {
+        BLog(BLOG_ERROR, "connection failed");
+        goto fail0;
+    }
+    
+    // set connected
+    o->connected = 1;
+    
+fail0:
+    // call handler
+    o->handler(o->user, !o->connected);
+    return;
+}
+
+static void connector_job_handler (BConnector *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->fd >= 0)
+    ASSERT(o->connected)
+    ASSERT(!o->have_bfd)
+    
+    // call handler
+    o->handler(o->user, 0);
+    return;
+}
+
+static void connection_report_error (BConnection *o)
+{
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->handler)
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR));
+    return;
+}
+
+static void connection_fd_handler (BConnection *o, int events)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    
+    // clear handled events
+    o->wait_events &= ~events;
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+    
+    int handled = 0;
+    
+    if ((events & BREACTOR_WRITE) || ((events & BREACTOR_ERROR) && o->send.inited && o->send.busy)) {
+        ASSERT(o->send.inited)
+        ASSERT(o->send.busy)
+        
+        // set handled
+        handled = 1;
+        
+        // set job
+        BPending_Set(&o->send.job);
+    }
+    
+    if ((events & BREACTOR_READ) || ((events & BREACTOR_ERROR) && o->recv.inited && o->recv.busy && !o->recv.closed)) {
+        ASSERT(o->recv.inited)
+        ASSERT(o->recv.busy)
+        ASSERT(!o->recv.closed)
+        
+        // set handled
+        handled = 1;
+        
+        // set job
+        BPending_Set(&o->recv.job);
+    }
+    
+    if (!handled) {
+        BLog(BLOG_ERROR, "fd error event");
+        connection_report_error(o);
+        return;
+    }
+}
+
+static void connection_send_job_handler (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->send.inited)
+    ASSERT(o->send.busy)
+    
+    // limit
+    if (!BReactorLimit_Increment(&o->send.limit)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_WRITE;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    
+    // send
+    int bytes = write(o->fd, o->send.busy_data, o->send.busy_data_len);
+    if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_WRITE;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    if (bytes < 0) {
+        BLog(BLOG_ERROR, "send failed");
+        connection_report_error(o);
+        return;
+    }
+    
+    ASSERT(bytes > 0)
+    ASSERT(bytes <= o->send.busy_data_len)
+    
+    // set not busy
+    o->send.busy = 0;
+    
+    // done
+    StreamPassInterface_Done(&o->send.iface, bytes);
+}
+
+static void connection_recv_job_handler (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->recv.inited)
+    ASSERT(o->recv.busy)
+    ASSERT(!o->recv.closed)
+    
+    // limit
+    if (!BReactorLimit_Increment(&o->recv.limit)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_READ;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    
+    // recv
+    int bytes = read(o->fd, o->recv.busy_data, o->recv.busy_data_avail);
+    if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_READ;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    if (bytes < 0) {
+        BLog(BLOG_ERROR, "recv failed");
+        connection_report_error(o);
+        return;
+    }
+    if (bytes == 0) {
+        // set recv closed
+        o->recv.closed = 1;
+        
+        // report recv closed
+        o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED);
+        return;
+    }
+    
+    ASSERT(bytes > 0)
+    ASSERT(bytes <= o->recv.busy_data_avail)
+    
+    // set not busy
+    o->recv.busy = 0;
+    
+    // done
+    StreamRecvInterface_Done(&o->recv.iface, bytes);
+}
+
+static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->send.inited)
+    ASSERT(!o->send.busy)
+    ASSERT(data_len > 0)
+    
+    // remember data
+    o->send.busy_data = data;
+    o->send.busy_data_len = data_len;
+    
+    // set busy
+    o->send.busy = 1;
+    
+    // set job
+    BPending_Set(&o->send.job);
+}
+
+static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_avail)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->recv.inited)
+    ASSERT(!o->recv.busy)
+    ASSERT(!o->recv.closed)
+    ASSERT(data_avail > 0)
+    
+    // remember data
+    o->recv.busy_data = data;
+    o->recv.busy_data_avail = data_avail;
+    
+    // set busy
+    o->recv.busy = 1;
+    
+    // set job
+    BPending_Set(&o->recv.job);
+}
+
+int BConnection_AddressSupported (BAddr addr)
+{
+    BAddr_Assert(&addr);
+    
+    return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6);
+}
+
+int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user,
+                    BListener_handler handler)
+{
+    ASSERT(BConnection_AddressSupported(addr))
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // convert address
+    struct sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, addr);
+    
+    // init fd
+    if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) {
+        BLog(BLOG_ERROR, "socket failed");
+        goto fail0;
+    }
+    
+    // set non-blocking
+    if (!badvpn_set_nonblocking(o->fd)) {
+        BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
+        goto fail1;
+    }
+    
+    // set SO_REUSEADDR
+    int optval = 1;
+    if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
+        BLog(BLOG_ERROR, "setsockopt(SO_REUSEADDR) failed");
+    }
+    
+    // bind
+    if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) {
+        BLog(BLOG_ERROR, "bind failed");
+        goto fail1;
+    }
+    
+    // listen
+    if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) {
+        BLog(BLOG_ERROR, "listen failed");
+        goto fail1;
+    }
+    
+    // init BFileDescriptor
+    BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o);
+    if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
+        BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+        goto fail1;
+    }
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
+    
+    // init default job
+    BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o);
+    
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    if (close(o->fd) < 0) {
+        BLog(BLOG_ERROR, "close failed");
+    }
+fail0:
+    return 0;
+}
+
+void BListener_Free (BListener *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    // free default job
+    BPending_Free(&o->default_job);
+    
+    // free BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    
+    // free fd
+    if (close(o->fd) < 0) {
+        BLog(BLOG_ERROR, "close failed");
+    }
+}
+
+int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user,
+                     BConnector_handler handler)
+{
+    ASSERT(BConnection_AddressSupported(addr))
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // convert address
+    struct sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, addr);
+    
+    // init job
+    BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o);
+    
+    // init fd
+    if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) {
+        BLog(BLOG_ERROR, "socket failed");
+        goto fail1;
+    }
+    
+    // set fd non-blocking
+    if (!badvpn_set_nonblocking(o->fd)) {
+        BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
+        goto fail2;
+    }
+    
+    // connect fd
+    int res = connect(o->fd, &sysaddr.addr.generic, sysaddr.len);
+    if (res < 0 && errno != EINPROGRESS) {
+        BLog(BLOG_ERROR, "connect failed");
+        goto fail2;
+    }
+    
+    // set not connected
+    o->connected = 0;
+    
+    // set have no BFileDescriptor
+    o->have_bfd = 0;
+    
+    if (res < 0) {
+        // init BFileDescriptor
+        BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o);
+        if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
+            BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+            goto fail2;
+        }
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE);
+        
+        // set have BFileDescriptor
+        o->have_bfd = 1;
+    } else {
+        // set connected
+        o->connected = 1;
+        
+        // set job
+        BPending_Set(&o->job);
+    }
+    
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail2:
+    if (close(o->fd) < 0) {
+        BLog(BLOG_ERROR, "close failed");
+    }
+fail1:
+    BPending_Free(&o->job);
+    return 0;
+}
+
+void BConnector_Free (BConnector *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    // free BFileDescriptor
+    if (o->have_bfd) {
+        BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    }
+    
+    // close fd
+    if (o->fd != -1) {
+        if (close(o->fd) < 0) {
+            BLog(BLOG_ERROR, "close failed");
+        }
+    }
+    
+    // free job
+    BPending_Free(&o->job);
+}
+
+int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user,
+                      BConnection_handler handler)
+{
+    switch (source.type) {
+        case BCONNECTION_SOURCE_TYPE_LISTENER: {
+            BListener *listener = source.u.listener.listener;
+            DebugObject_Access(&listener->d_obj);
+            ASSERT(BPending_IsSet(&listener->default_job))
+        } break;
+        case BCONNECTION_SOURCE_TYPE_CONNECTOR: {
+            BConnector *connector = source.u.connector.connector;
+            DebugObject_Access(&connector->d_obj);
+            ASSERT(connector->fd >= 0)
+            ASSERT(connector->connected)
+            ASSERT(!connector->have_bfd)
+            ASSERT(!BPending_IsSet(&connector->job))
+        } break;
+        case BCONNECTION_SOURCE_TYPE_PIPE: {
+            ASSERT(source.u.pipe.pipefd >= 0)
+        } break;
+        default: ASSERT(0);
+    }
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    switch (source.type) {
+        case BCONNECTION_SOURCE_TYPE_LISTENER: {
+            BListener *listener = source.u.listener.listener;
+            
+            // unset listener's default job
+            BPending_Unset(&listener->default_job);
+            
+            // accept
+            struct sys_addr sysaddr;
+            sysaddr.len = sizeof(sysaddr.addr);
+            if ((o->fd = accept(listener->fd, &sysaddr.addr.generic, &sysaddr.len)) < 0) {
+                BLog(BLOG_ERROR, "accept failed");
+                goto fail0;
+            }
+            o->close_fd = 1;
+            
+            // set non-blocking
+            if (!badvpn_set_nonblocking(o->fd)) {
+                BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
+                goto fail1;
+            }
+            
+            // return address
+            if (source.u.listener.out_addr) {
+                addr_sys_to_socket(source.u.listener.out_addr, sysaddr);
+            }
+        } break;
+        
+        case BCONNECTION_SOURCE_TYPE_CONNECTOR: {
+            BConnector *connector = source.u.connector.connector;
+            
+            // grab fd from connector
+            o->fd = connector->fd;
+            connector->fd = -1;
+            o->close_fd = 1;
+        } break;
+        
+        case BCONNECTION_SOURCE_TYPE_PIPE: {
+            // use user-provided fd
+            o->fd = source.u.pipe.pipefd;
+            o->close_fd = 0;
+            
+            // set non-blocking
+            if (!badvpn_set_nonblocking(o->fd)) {
+                BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
+                goto fail1;
+            }
+        } break;
+    }
+    
+    // init BFileDescriptor
+    BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connection_fd_handler, o);
+    if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
+        BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+        goto fail1;
+    }
+    
+    // set no wait events
+    o->wait_events = 0;
+    
+    // init limits
+    BReactorLimit_Init(&o->send.limit, o->reactor, BCONNECTION_SEND_LIMIT);
+    BReactorLimit_Init(&o->recv.limit, o->reactor, BCONNECTION_RECV_LIMIT);
+    
+    // set send and recv not inited
+    o->send.inited = 0;
+    o->recv.inited = 0;
+    
+    // set recv not closed
+    o->recv.closed = 0;
+    
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    if (o->close_fd) {
+        if (close(o->fd) < 0) {
+            BLog(BLOG_ERROR, "close failed");
+        }
+    }
+fail0:
+    return 0;
+}
+
+void BConnection_Free (BConnection *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
+    ASSERT(!o->recv.inited)
+    ASSERT(!o->send.inited)
+    
+    // free limits
+    BReactorLimit_Free(&o->recv.limit);
+    BReactorLimit_Free(&o->send.limit);
+    
+    // free BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    
+    // close fd
+    if (o->close_fd) {
+        if (close(o->fd) < 0) {
+            BLog(BLOG_ERROR, "close failed");
+        }
+    }
+}
+
+void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler_event)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    // set handlers
+    o->user = user;
+    o->handler = handler_event;
+}
+
+int BConnection_SetSendBuffer (BConnection *o, int buf_size)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    if (setsockopt(o->fd, SOL_SOCKET, SO_SNDBUF, (void *)&buf_size, sizeof(buf_size)) < 0) {
+        BLog(BLOG_ERROR, "setsockopt failed");
+        return 0;
+    }
+    
+    return 1;
+}
+
+void BConnection_SendAsync_Init (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->send.inited)
+    
+    // init interface
+    StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, BReactor_PendingGroup(o->reactor));
+    
+    // init job
+    BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_send_job_handler, o);
+    
+    // set not busy
+    o->send.busy = 0;
+    
+    // set inited
+    o->send.inited = 1;
+}
+
+void BConnection_SendAsync_Free (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    // update events
+    o->wait_events &= ~BREACTOR_WRITE;
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+    
+    // free job
+    BPending_Free(&o->send.job);
+    
+    // free interface
+    StreamPassInterface_Free(&o->send.iface);
+    
+    // set not inited
+    o->send.inited = 0;
+}
+
+StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    return &o->send.iface;
+}
+
+void BConnection_RecvAsync_Init (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->recv.inited)
+    ASSERT(!o->recv.closed)
+    
+    // init interface
+    StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor));
+    
+    // init job
+    BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_recv_job_handler, o);
+    
+    // set not busy
+    o->recv.busy = 0;
+    
+    // set inited
+    o->recv.inited = 1;
+}
+
+void BConnection_RecvAsync_Free (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    // update events
+    o->wait_events &= ~BREACTOR_READ;
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+    
+    // free job
+    BPending_Free(&o->recv.job);
+    
+    // free interface
+    StreamRecvInterface_Free(&o->recv.iface);
+    
+    // set not inited
+    o->recv.inited = 0;
+}
+
+StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    return &o->recv.iface;
+}

+ 81 - 0
system/BConnection_unix.h

@@ -0,0 +1,81 @@
+/**
+ * @file BConnection_unix.h
+ * @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/debugerror.h>
+#include <base/DebugObject.h>
+
+#define BCONNECTION_SEND_LIMIT 2
+#define BCONNECTION_RECV_LIMIT 2
+#define BCONNECTION_LISTEN_BACKLOG 128
+
+struct BListener_s {
+    BReactor *reactor;
+    void *user;
+    BListener_handler handler;
+    int fd;
+    BFileDescriptor bfd;
+    BPending default_job;
+    DebugObject d_obj;
+};
+
+struct BConnector_s {
+    BReactor *reactor;
+    void *user;
+    BConnector_handler handler;
+    BPending job;
+    int fd;
+    int connected;
+    int have_bfd;
+    BFileDescriptor bfd;
+    DebugObject d_obj;
+};
+
+struct BConnection_s {
+    BReactor *reactor;
+    void *user;
+    BConnection_handler handler;
+    int fd;
+    int close_fd;
+    BFileDescriptor bfd;
+    int wait_events;
+    struct {
+        BReactorLimit limit;
+        int inited;
+        StreamPassInterface iface;
+        BPending job;
+        int busy;
+        const uint8_t *busy_data;
+        int busy_data_len;
+    } send;
+    struct {
+        BReactorLimit limit;
+        int inited;
+        int closed;
+        StreamRecvInterface iface;
+        BPending job;
+        int busy;
+        uint8_t *busy_data;
+        int busy_data_avail;
+    } recv;
+    DebugError d_err;
+    DebugObject d_obj;
+};

+ 858 - 0
system/BConnection_win.c

@@ -0,0 +1,858 @@
+/**
+ * @file BConnection_win.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 <stdlib.h>
+#include <limits.h>
+
+#include <base/BLog.h>
+
+#include "BConnection.h"
+
+#include <generated/blog_channel_BConnection.h>
+
+#define LISTEN_BACKLOG 128
+
+struct sys_addr {
+    int len;
+    union {
+        struct sockaddr generic;
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } addr;
+};
+
+static void addr_socket_to_sys (struct sys_addr *out, BAddr addr);
+static void addr_any_to_sys (struct sys_addr *out, int family);
+static void addr_sys_to_socket (BAddr *out, struct sys_addr addr);
+static void listener_next_job_handler (BListener *o);
+static void listener_olap_handler (BListener *o, int event, DWORD bytes);
+static void connector_olap_handler (BConnector *o, int event, DWORD bytes);
+static void connector_abort (BConnector *o);
+static void connection_report_error (BConnection *o);
+static void connection_abort (BConnection *o);
+static void connection_send_iface_handler_send (BConnection *o, uint8_t *data, int data_len);
+static void connection_recv_iface_handler_recv (BConnection *o, uint8_t *data, int data_len);
+static void connection_send_olap_handler (BConnection *o, int event, DWORD bytes);
+static void connection_recv_olap_handler (BConnection *o, int event, DWORD bytes);
+
+static void addr_socket_to_sys (struct sys_addr *out, BAddr addr)
+{
+    switch (addr.type) {
+        case BADDR_TYPE_IPV4: {
+            out->len = sizeof(out->addr.ipv4);
+            memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
+            out->addr.ipv4.sin_family = AF_INET;
+            out->addr.ipv4.sin_port = addr.ipv4.port;
+            out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip;
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            out->len = sizeof(out->addr.ipv6);
+            memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
+            out->addr.ipv6.sin6_family = AF_INET6;
+            out->addr.ipv6.sin6_port = addr.ipv6.port;
+            out->addr.ipv6.sin6_flowinfo = 0;
+            memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16);
+            out->addr.ipv6.sin6_scope_id = 0;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+static void addr_any_to_sys (struct sys_addr *out, int family)
+{
+    switch (family) {
+        case BADDR_TYPE_IPV4: {
+            out->len = sizeof(out->addr.ipv4);
+            memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
+            out->addr.ipv4.sin_family = AF_INET;
+            out->addr.ipv4.sin_port = 0;
+            out->addr.ipv4.sin_addr.s_addr = INADDR_ANY;
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            out->len = sizeof(out->addr.ipv6);
+            memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
+            out->addr.ipv6.sin6_family = AF_INET6;
+            out->addr.ipv6.sin6_port = 0;
+            out->addr.ipv6.sin6_flowinfo = 0;
+            out->addr.ipv6.sin6_addr = (struct in6_addr)IN6ADDR_ANY_INIT;
+            out->addr.ipv6.sin6_scope_id = 0;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+static void addr_sys_to_socket (BAddr *out, struct sys_addr addr)
+{
+    switch (addr.addr.generic.sa_family) {
+        case AF_INET: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in))
+            BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port);
+        } break;
+        
+        case AF_INET6: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in6))
+            BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port);
+        } break;
+        
+        default: {
+            BAddr_InitNone(out);
+        } break;
+    }
+}
+
+static void listener_next_job_handler (BListener *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->busy)
+    
+    // free ready socket
+    if (o->ready) {
+        BLog(BLOG_ERROR, "discarding connection");
+        
+        // close new socket
+        if (closesocket(o->newsock) == SOCKET_ERROR) {
+            BLog(BLOG_ERROR, "closesocket failed");
+        }
+        
+        // set not ready
+        o->ready = 0;
+    }
+    
+    // create new socket
+    if ((o->newsock = WSASocket(o->sys_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
+        BLog(BLOG_ERROR, "WSASocket failed");
+        goto fail0;
+    }
+    
+    // start accept operation
+    while (1) {
+        memset(&o->olap.olap, 0, sizeof(o->olap.olap));
+        DWORD bytes;
+        BOOL res = o->fnAcceptEx(o->sock, o->newsock, o->addrbuf, 0, sizeof(struct BListener_addrbuf_stub), sizeof(struct BListener_addrbuf_stub), &bytes, &o->olap.olap);
+        if (res == FALSE && WSAGetLastError() != ERROR_IO_PENDING) {
+            BLog(BLOG_ERROR, "AcceptEx failed");
+            continue;
+        }
+        break;
+    }
+    
+    // set busy
+    o->busy = 1;
+    
+    return;
+    
+fail0:
+    return;
+}
+
+static void listener_olap_handler (BListener *o, int event, DWORD bytes)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->busy)
+    ASSERT(!o->ready)
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
+    
+    // set not busy
+    o->busy = 0;
+    
+    // schedule next accept
+    BPending_Set(&o->next_job);
+    
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "accepting failed");
+        
+        // close new socket
+        if (closesocket(o->newsock) == SOCKET_ERROR) {
+            BLog(BLOG_ERROR, "closesocket failed");
+        }
+        
+        return;
+    }
+    
+    BLog(BLOG_INFO, "connection accepted");
+    
+    // set ready
+    o->ready = 1;
+    
+    // call handler
+    o->handler(o->user);
+    return;
+}
+
+static void connector_olap_handler (BConnector *o, int event, DWORD bytes)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->sock != INVALID_SOCKET)
+    ASSERT(o->busy)
+    ASSERT(!o->ready)
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
+    
+    // set not busy
+    o->busy = 0;
+    
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "connection failed");
+    } else {
+        // set ready
+        o->ready = 1;
+    }
+    
+    // call handler
+    o->handler(o->user, !o->ready);
+    return;
+}
+
+static void connector_abort (BConnector *o)
+{
+    if (o->sock != INVALID_SOCKET) {
+        // cancel I/O
+        if (o->busy) {
+            if (!CancelIo((HANDLE)o->sock)) {
+                BLog(BLOG_ERROR, "CancelIo failed");
+            }
+        }
+        
+        // close socket
+        if (closesocket(o->sock) == SOCKET_ERROR) {
+            BLog(BLOG_ERROR, "closesocket failed");
+        }
+    }
+    
+    // wait for connect operation to finish
+    if (o->busy) {
+        BReactorIOCPOverlapped_Wait(&o->olap, NULL, NULL);
+    }
+    
+    // free olap
+    BReactorIOCPOverlapped_Free(&o->olap);
+}
+
+static void connection_report_error (BConnection *o)
+{
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->handler)
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR));
+    return;
+}
+
+static void connection_abort (BConnection *o)
+{
+    ASSERT(!o->aborted)
+    
+    // cancel I/O
+    if ((o->recv.inited && o->recv.busy) || (o->send.inited && o->send.busy)) {
+        if (!CancelIo((HANDLE)o->sock)) {
+            BLog(BLOG_ERROR, "CancelIo failed");
+        }
+    }
+    
+    // close socket
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+    
+    // wait for receiving to complete
+    if (o->recv.inited && o->recv.busy) {
+        BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL);
+    }
+    
+    // wait for sending to complete
+    if (o->send.inited && o->send.busy) {
+        BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL);
+    }
+    
+    // free recv olap
+    BReactorIOCPOverlapped_Free(&o->recv.olap);
+    
+    // free send olap
+    BReactorIOCPOverlapped_Free(&o->send.olap);
+    
+    // set aborted
+    o->aborted = 1;
+}
+
+static void connection_send_iface_handler_send (BConnection *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->send.inited)
+    ASSERT(!o->send.busy)
+    ASSERT(data_len > 0)
+    
+    if (data_len > ULONG_MAX) {
+        data_len = ULONG_MAX;
+    }
+    
+    WSABUF buf;
+    buf.buf = data;
+    buf.len = data_len;
+    
+    memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap));
+    
+    // send
+    int res = WSASend(o->sock, &buf, 1, NULL, 0, &o->send.olap.olap, NULL);
+    if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
+        BLog(BLOG_ERROR, "WSASend failed (%d)", WSAGetLastError());
+        connection_report_error(o);
+        return;
+    }
+    
+    // set busy
+    o->send.busy = 1;
+    o->send.busy_data_len = data_len;
+}
+
+static void connection_recv_iface_handler_recv (BConnection *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->recv.closed)
+    ASSERT(!o->aborted)
+    ASSERT(o->recv.inited)
+    ASSERT(!o->recv.busy)
+    ASSERT(data_len > 0)
+    
+    if (data_len > ULONG_MAX) {
+        data_len = ULONG_MAX;
+    }
+    
+    WSABUF buf;
+    buf.buf = data;
+    buf.len = data_len;
+    
+    memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap));
+    
+    // recv
+    DWORD flags = 0;
+    int res = WSARecv(o->sock, &buf, 1, NULL, &flags, &o->recv.olap.olap, NULL);
+    if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
+        BLog(BLOG_ERROR, "WSARecv failed (%d)", WSAGetLastError());
+        connection_report_error(o);
+        return;
+    }
+    
+    // set busy
+    o->recv.busy = 1;
+    o->recv.busy_data_len = data_len;
+}
+
+static void connection_send_olap_handler (BConnection *o, int event, DWORD bytes)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->send.inited)
+    ASSERT(o->send.busy)
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
+    
+    // set not busy
+    o->send.busy = 0;
+    
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "sending failed");
+        connection_report_error(o);
+        return;
+    }
+    
+    ASSERT(bytes > 0)
+    ASSERT(bytes <= o->send.busy_data_len)
+    
+    // done
+    StreamPassInterface_Done(&o->send.iface, bytes);
+}
+
+static void connection_recv_olap_handler (BConnection *o, int event, DWORD bytes)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->recv.closed)
+    ASSERT(!o->aborted)
+    ASSERT(o->recv.inited)
+    ASSERT(o->recv.busy)
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
+    
+    // set not busy
+    o->recv.busy = 0;
+    
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "receiving failed");
+        connection_report_error(o);
+        return;
+    }
+    
+    if (bytes == 0) {
+        // set closed
+        o->recv.closed = 1;
+        
+        // report recv closed
+        o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED);
+        return;
+    }
+    
+    ASSERT(bytes > 0)
+    ASSERT(bytes <= o->recv.busy_data_len)
+    
+    // done
+    StreamRecvInterface_Done(&o->recv.iface, bytes);
+}
+
+int BConnection_AddressSupported (BAddr addr)
+{
+    BAddr_Assert(&addr);
+    
+    return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6);
+}
+
+int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user,
+                    BListener_handler handler)
+{
+    ASSERT(BConnection_AddressSupported(addr))
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // convert address
+    struct sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, addr);
+    
+    // remember family
+    o->sys_family = sysaddr.addr.generic.sa_family;
+    
+    // init socket
+    if ((o->sock = WSASocket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
+        BLog(BLOG_ERROR, "WSASocket failed");
+        goto fail0;
+    }
+    
+    // associate with IOCP
+    if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) {
+        BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
+        goto fail1;
+    }
+    
+    // bind
+    if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) {
+        BLog(BLOG_ERROR, "bind failed");
+        goto fail1;
+    }
+    
+    // listen
+    if (listen(o->sock, LISTEN_BACKLOG) < 0) {
+        BLog(BLOG_ERROR, "listen failed");
+        goto fail1;
+    }
+    
+    GUID guid;
+    DWORD out_bytes;
+    
+    // obtain AcceptEx
+    guid = (GUID)WSAID_ACCEPTEX;
+    if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnAcceptEx, sizeof(o->fnAcceptEx), &out_bytes, NULL, NULL) != 0) {
+        BLog(BLOG_ERROR, "faild to obtain AcceptEx");
+        goto fail1;
+    }
+    
+    // obtain GetAcceptExSockaddrs
+    guid = (GUID)WSAID_GETACCEPTEXSOCKADDRS;
+    if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnGetAcceptExSockaddrs, sizeof(o->fnGetAcceptExSockaddrs), &out_bytes, NULL, NULL) != 0) {
+        BLog(BLOG_ERROR, "faild to obtain GetAcceptExSockaddrs");
+        goto fail1;
+    }
+    
+    // init olap
+    BReactorIOCPOverlapped_Init(&o->olap, o->reactor, o, (BReactorIOCPOverlapped_handler)listener_olap_handler);
+    
+    // init next job
+    BPending_Init(&o->next_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_next_job_handler, o);
+    
+    // set not busy
+    o->busy = 0;
+    
+    // set not ready
+    o->ready = 0;
+    
+    // set next job
+    BPending_Set(&o->next_job);
+    
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+fail0:
+    return 0;
+}
+
+void BListener_Free (BListener *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    // cancel I/O
+    if (o->busy) {
+        if (!CancelIo((HANDLE)o->sock)) {
+            BLog(BLOG_ERROR, "CancelIo failed");
+        }
+    }
+    
+    // close socket
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+    
+    // wait for accept operation to finish
+    if (o->busy) {
+        BReactorIOCPOverlapped_Wait(&o->olap, NULL, NULL);
+    }
+    
+    // close new socket
+    if (o->busy || o->ready) {
+        if (closesocket(o->newsock) == SOCKET_ERROR) {
+            BLog(BLOG_ERROR, "closesocket failed");
+        }
+    }
+    
+    // free next job
+    BPending_Free(&o->next_job);
+    
+    // free olap
+    BReactorIOCPOverlapped_Free(&o->olap);
+}
+
+int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user,
+                     BConnector_handler handler)
+{
+    ASSERT(BConnection_AddressSupported(addr))
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // convert address
+    struct sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, addr);
+    
+    // create local any address
+    struct sys_addr local_sysaddr;
+    addr_any_to_sys(&local_sysaddr, addr.type);
+    
+    // init socket
+    if ((o->sock = WSASocket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
+        BLog(BLOG_ERROR, "WSASocket failed");
+        goto fail0;
+    }
+    
+    // associate with IOCP
+    if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) {
+        BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
+        goto fail1;
+    }
+    
+    // bind socket
+    if (bind(o->sock, &local_sysaddr.addr.generic, local_sysaddr.len) < 0) {
+        BLog(BLOG_ERROR, "bind failed");
+        goto fail1;
+    }
+    
+    // obtain ConnectEx
+    GUID guid = WSAID_CONNECTEX;
+    DWORD out_bytes;
+    if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnConnectEx, sizeof(o->fnConnectEx), &out_bytes, NULL, NULL) != 0) {
+        BLog(BLOG_ERROR, "faild to get ConnectEx");
+        goto fail1;
+    }
+    
+    // init olap
+    BReactorIOCPOverlapped_Init(&o->olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connector_olap_handler);
+    
+    // start connect operation
+    BOOL res = o->fnConnectEx(o->sock, &sysaddr.addr.generic, sysaddr.len, NULL, 0, NULL, &o->olap.olap);
+    if (res == FALSE && WSAGetLastError() != ERROR_IO_PENDING) {
+        BLog(BLOG_ERROR, "ConnectEx failed (%d)", WSAGetLastError());
+        goto fail2;
+    }
+    
+    // set busy
+    o->busy = 1;
+    
+    // set not ready
+    o->ready = 0;
+    
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail2:
+    BReactorIOCPOverlapped_Free(&o->olap);
+fail1:
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+fail0:
+    return 0;
+}
+
+void BConnector_Free (BConnector *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    if (o->sock != INVALID_SOCKET) {
+        connector_abort(o);
+    }
+}
+
+int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user,
+                      BConnection_handler handler)
+{
+    switch (source.type) {
+        case BCONNECTION_SOURCE_TYPE_LISTENER: {
+            BListener *listener = source.u.listener.listener;
+            DebugObject_Access(&listener->d_obj);
+            ASSERT(BPending_IsSet(&listener->next_job))
+            ASSERT(!listener->busy)
+            ASSERT(listener->ready)
+        } break;
+        case BCONNECTION_SOURCE_TYPE_CONNECTOR: {
+            BConnector *connector = source.u.connector.connector;
+            DebugObject_Access(&connector->d_obj);
+            ASSERT(connector->reactor == reactor)
+            ASSERT(connector->sock != INVALID_SOCKET)
+            ASSERT(!connector->busy)
+            ASSERT(connector->ready)
+        } break;
+        default: ASSERT(0);
+    }
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    switch (source.type) {
+        case BCONNECTION_SOURCE_TYPE_LISTENER: {
+            BListener *listener = source.u.listener.listener;
+            
+            // grab new socket from listener
+            o->sock = listener->newsock;
+            listener->ready = 0;
+            
+            // associate with IOCP
+            if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) {
+                BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
+                goto fail1;
+            }
+            
+            // return address
+            if (source.u.listener.out_addr) {
+                struct sockaddr *addr_local;
+                struct sockaddr *addr_remote;
+                int len_local;
+                int len_remote;
+                listener->fnGetAcceptExSockaddrs(listener->addrbuf, 0, sizeof(struct BListener_addrbuf_stub), sizeof(struct BListener_addrbuf_stub),
+                                                 &addr_local, &len_local, &addr_remote, &len_remote);
+                
+                struct sys_addr sysaddr;
+                
+                ASSERT_FORCE(len_remote >= 0)
+                ASSERT_FORCE(len_remote <= sizeof(sysaddr.addr))
+                
+                memcpy((uint8_t *)&sysaddr.addr, (uint8_t *)addr_remote, len_remote);
+                sysaddr.len = len_remote;
+                
+                addr_sys_to_socket(source.u.listener.out_addr, sysaddr);
+            }
+        } break;
+        
+        case BCONNECTION_SOURCE_TYPE_CONNECTOR: {
+            BConnector *connector = source.u.connector.connector;
+            
+            // grab fd from connector
+            o->sock = connector->sock;
+            connector->sock = INVALID_SOCKET;
+            
+            // release connector resources
+            connector_abort(connector);
+        } break;
+    }
+    
+    // set not aborted
+    o->aborted = 0;
+    
+    // init send olap
+    BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connection_send_olap_handler);
+    
+    // set send not inited
+    o->send.inited = 0;
+    
+    // init recv olap
+    BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connection_recv_olap_handler);
+    
+    // set recv not closed
+    o->recv.closed = 0;
+    
+    // set recv not inited
+    o->recv.inited = 0;
+    
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+    return 0;
+}
+
+void BConnection_Free (BConnection *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
+    ASSERT(!o->recv.inited)
+    ASSERT(!o->send.inited)
+    
+    if (!o->aborted) {
+        connection_abort(o);
+    }
+}
+
+void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler_event)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    // set handlers
+    o->user = user;
+    o->handler = handler_event;
+}
+
+int BConnection_SetSendBuffer (BConnection *o, int buf_size)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    if (setsockopt(o->sock, SOL_SOCKET, SO_SNDBUF, (void *)&buf_size, sizeof(buf_size)) < 0) {
+        BLog(BLOG_ERROR, "setsockopt failed");
+        return 0;
+    }
+    
+    return 1;
+}
+
+void BConnection_SendAsync_Init (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(!o->send.inited)
+    
+    // init interface
+    StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_iface_handler_send, o, BReactor_PendingGroup(o->reactor));
+    
+    // set not busy
+    o->send.busy = 0;
+    
+    // set inited
+    o->send.inited = 1;
+}
+
+void BConnection_SendAsync_Free (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    // abort if busy
+    if (o->send.busy && !o->aborted) {
+        connection_abort(o);
+    }
+    
+    // free interface
+    StreamPassInterface_Free(&o->send.iface);
+    
+    // set not inited
+    o->send.inited = 0;
+}
+
+StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    return &o->send.iface;
+}
+
+void BConnection_RecvAsync_Init (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->recv.closed)
+    ASSERT(!o->aborted)
+    ASSERT(!o->recv.inited)
+    
+    // init interface
+    StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_iface_handler_recv, o, BReactor_PendingGroup(o->reactor));
+    
+    // set not busy
+    o->recv.busy = 0;
+    
+    // set inited
+    o->recv.inited = 1;
+}
+
+void BConnection_RecvAsync_Free (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    // abort if busy
+    if (o->recv.busy && !o->aborted) {
+        connection_abort(o);
+    }
+    
+    // free interface
+    StreamRecvInterface_Free(&o->recv.iface);
+    
+    // set not inited
+    o->recv.inited = 0;
+}
+
+StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    return &o->recv.iface;
+}

+ 94 - 0
system/BConnection_win.h

@@ -0,0 +1,94 @@
+/**
+ * @file BConnection_win.h
+ * @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 <windows.h>
+#include <winsock2.h>
+#ifdef BADVPN_USE_SHIPPED_MSWSOCK
+#    include <misc/mswsock.h>
+#else
+#    include <mswsock.h>
+#endif
+
+#include <misc/debugerror.h>
+#include <base/DebugObject.h>
+
+struct BListener_addrbuf_stub {
+    union {
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } addr;
+    uint8_t extra[16];
+};
+
+struct BListener_s {
+    BReactor *reactor;
+    void *user;
+    BListener_handler handler;
+    int sys_family;
+    SOCKET sock;
+    LPFN_ACCEPTEX fnAcceptEx;
+    LPFN_GETACCEPTEXSOCKADDRS fnGetAcceptExSockaddrs;
+    BReactorIOCPOverlapped olap;
+    SOCKET newsock;
+    uint8_t addrbuf[2 * sizeof(struct BListener_addrbuf_stub)];
+    BPending next_job;
+    int busy;
+    int ready;
+    DebugObject d_obj;
+};
+
+struct BConnector_s {
+    BReactor *reactor;
+    void *user;
+    BConnector_handler handler;
+    SOCKET sock;
+    LPFN_CONNECTEX fnConnectEx;
+    BReactorIOCPOverlapped olap;
+    int busy;
+    int ready;
+    DebugObject d_obj;
+};
+
+struct BConnection_s {
+    BReactor *reactor;
+    void *user;
+    BConnection_handler handler;
+    SOCKET sock;
+    int aborted;
+    struct {
+        BReactorIOCPOverlapped olap;
+        int inited;
+        StreamPassInterface iface;
+        int busy;
+        int busy_data_len;
+    } send;
+    struct {
+        BReactorIOCPOverlapped olap;
+        int closed;
+        int inited;
+        StreamRecvInterface iface;
+        int busy;
+        int busy_data_len;
+    } recv;
+    DebugError d_err;
+    DebugObject d_obj;
+};

+ 66 - 0
system/BDatagram.h

@@ -0,0 +1,66 @@
+/**
+ * @file BDatagram.h
+ * @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.
+ */
+
+#ifndef BADVPN_SYSTEM_BDATAGRAM
+#define BADVPN_SYSTEM_BDATAGRAM
+
+#include <misc/debug.h>
+#include <flow/PacketPassInterface.h>
+#include <flow/PacketRecvInterface.h>
+#include <system/BAddr.h>
+#include <system/BReactor.h>
+#include <system/BNetwork.h>
+
+struct BDatagram_s;
+typedef struct BDatagram_s BDatagram;
+
+#define BDATAGRAM_EVENT_ERROR 1
+
+typedef void (*BDatagram_handler) (void *user, int event);
+
+int BDatagram_AddressFamilySupported (int family);
+
+int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user,
+                    BDatagram_handler handler) WARN_UNUSED;
+void BDatagram_Free (BDatagram *o);
+int BDatagram_Bind (BDatagram *o, BAddr addr) WARN_UNUSED;
+void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr);
+int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr) WARN_UNUSED;
+#ifndef BADVPN_USE_WINAPI
+int BDatagram_GetFd (BDatagram *o);
+#endif
+
+void BDatagram_SendAsync_Init (BDatagram *o, int mtu);
+void BDatagram_SendAsync_Free (BDatagram *o);
+PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o);
+
+void BDatagram_RecvAsync_Init (BDatagram *o, int mtu);
+void BDatagram_RecvAsync_Free (BDatagram *o);
+PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o);
+
+#ifdef BADVPN_USE_WINAPI
+#include "BDatagram_win.h"
+#else
+#include "BDatagram_unix.h"
+#endif
+
+#endif

+ 769 - 0
system/BDatagram_unix.c

@@ -0,0 +1,769 @@
+/**
+ * @file BDatagram_unix.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.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef BADVPN_LINUX
+#    include <netpacket/packet.h>
+#    include <net/ethernet.h>
+#endif
+
+#include <misc/nonblocking.h>
+#include <base/BLog.h>
+
+#include "BDatagram.h"
+
+#include <generated/blog_channel_BDatagram.h>
+
+struct sys_addr {
+    socklen_t len;
+    union {
+        struct sockaddr generic;
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+#ifdef BADVPN_LINUX
+        struct sockaddr_ll packet;
+#endif
+    } addr;
+};
+
+static int family_socket_to_sys (int family);
+static void addr_socket_to_sys (struct sys_addr *out, BAddr addr);
+static void addr_sys_to_socket (BAddr *out, struct sys_addr addr);
+static void set_pktinfo (int fd, int family);
+static void report_error (BDatagram *o);
+static void fd_handler (BDatagram *o, int events);
+static void send_job_handler (BDatagram *o);
+static void recv_job_handler (BDatagram *o);
+static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len);
+static void recv_if_handler_recv (BDatagram *o, uint8_t *data);
+
+static int family_socket_to_sys (int family)
+{
+    switch (family) {
+        case BADDR_TYPE_IPV4:
+            return AF_INET;
+        case BADDR_TYPE_IPV6:
+            return AF_INET6;
+#ifdef BADVPN_LINUX
+        case BADDR_TYPE_PACKET:
+            return AF_PACKET;
+#endif
+    }
+    
+    ASSERT(0);
+    return 0;
+}
+
+static void addr_socket_to_sys (struct sys_addr *out, BAddr addr)
+{
+    switch (addr.type) {
+        case BADDR_TYPE_IPV4: {
+            out->len = sizeof(out->addr.ipv4);
+            memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
+            out->addr.ipv4.sin_family = AF_INET;
+            out->addr.ipv4.sin_port = addr.ipv4.port;
+            out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip;
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            out->len = sizeof(out->addr.ipv6);
+            memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
+            out->addr.ipv6.sin6_family = AF_INET6;
+            out->addr.ipv6.sin6_port = addr.ipv6.port;
+            out->addr.ipv6.sin6_flowinfo = 0;
+            memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16);
+            out->addr.ipv6.sin6_scope_id = 0;
+        } break;
+        
+#ifdef BADVPN_LINUX
+        case BADDR_TYPE_PACKET: {
+            ASSERT(addr.packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET)
+            memset(&out->addr.packet, 0, sizeof(out->addr.packet));
+            out->len = sizeof(out->addr.packet);
+            out->addr.packet.sll_family = AF_PACKET;
+            out->addr.packet.sll_protocol = addr.packet.phys_proto;
+            out->addr.packet.sll_ifindex = addr.packet.interface_index;
+            out->addr.packet.sll_hatype = 1; // linux/if_arp.h: #define ARPHRD_ETHER 1
+            switch (addr.packet.packet_type) {
+                case BADDR_PACKET_PACKET_TYPE_HOST:
+                    out->addr.packet.sll_pkttype = PACKET_HOST;
+                    break;
+                case BADDR_PACKET_PACKET_TYPE_BROADCAST:
+                    out->addr.packet.sll_pkttype = PACKET_BROADCAST;
+                    break;
+                case BADDR_PACKET_PACKET_TYPE_MULTICAST:
+                    out->addr.packet.sll_pkttype = PACKET_MULTICAST;
+                    break;
+                case BADDR_PACKET_PACKET_TYPE_OTHERHOST:
+                    out->addr.packet.sll_pkttype = PACKET_OTHERHOST;
+                    break;
+                case BADDR_PACKET_PACKET_TYPE_OUTGOING:
+                    out->addr.packet.sll_pkttype = PACKET_OUTGOING;
+                    break;
+                default:
+                    ASSERT(0);
+            }
+            out->addr.packet.sll_halen = 6;
+            memcpy(out->addr.packet.sll_addr, addr.packet.phys_addr, 6);
+        } break;
+#endif
+        
+        default: ASSERT(0);
+    }
+}
+
+static void addr_sys_to_socket (BAddr *out, struct sys_addr addr)
+{
+    switch (addr.addr.generic.sa_family) {
+        case AF_INET: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in))
+            BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port);
+        } break;
+        
+        case AF_INET6: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in6))
+            BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port);
+        } break;
+        
+        default: {
+            BAddr_InitNone(out);
+        } break;
+    }
+}
+
+static void set_pktinfo (int fd, int family)
+{
+    int opt = 1;
+    
+    switch (family) {
+        case BADDR_TYPE_IPV4: {
+#ifdef BADVPN_FREEBSD
+            if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) < 0) {
+                BLog(BLOG_ERROR, "setsockopt(IP_RECVDSTADDR) failed");
+            }
+#else
+            if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) < 0) {
+                BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed");
+            }
+#endif
+        } break;
+        
+#ifdef IPV6_RECVPKTINFO
+        case BADDR_TYPE_IPV6: {
+            if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) < 0) {
+                BLog(BLOG_ERROR, "setsockopt(IPV6_RECVPKTINFO) failed");
+            }
+        } break;
+#endif
+    }
+}
+
+static void report_error (BDatagram *o)
+{
+    DebugError_AssertNoError(&o->d_err);
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR));
+    return;
+}
+
+static void fd_handler (BDatagram *o, int events)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    
+    // clear handled events
+    o->wait_events &= ~events;
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+    
+    int handled = 0;
+    
+    if ((events & BREACTOR_WRITE) || ((events & BREACTOR_ERROR) && o->send.inited && o->send.busy && o->send.have_addrs)) {
+        ASSERT(o->send.inited)
+        ASSERT(o->send.busy)
+        ASSERT(o->send.have_addrs)
+        
+        // set handled
+        handled = 1;
+        
+        // set job
+        BPending_Set(&o->send.job);
+    }
+    
+    if ((events & BREACTOR_READ) || ((events & BREACTOR_ERROR) && o->recv.inited && o->recv.busy && o->recv.started)) {
+        ASSERT(o->recv.inited)
+        ASSERT(o->recv.busy)
+        ASSERT(o->recv.started)
+        
+        // set handled
+        handled = 1;
+        
+        // set job
+        BPending_Set(&o->recv.job);
+    }
+    
+    if (!handled) {
+        BLog(BLOG_ERROR, "fd error event");
+        report_error(o);
+        return;
+    }
+}
+
+static void send_job_handler (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->send.inited)
+    ASSERT(o->send.busy)
+    ASSERT(o->send.have_addrs)
+    
+    // limit
+    if (!BReactorLimit_Increment(&o->send.limit)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_WRITE;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    
+    // convert destination address
+    struct sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, o->send.remote_addr);
+    
+    struct iovec iov;
+    iov.iov_base = (uint8_t *)o->send.busy_data;
+    iov.iov_len = o->send.busy_data_len;
+    
+    union {
+#ifdef BADVPN_FREEBSD
+        char in[CMSG_SPACE(sizeof(struct in_addr))];
+#else
+        char in[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#endif
+        char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+    } cdata;
+    
+    struct msghdr msg;
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = &sysaddr.addr.generic;
+    msg.msg_namelen = sysaddr.len;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = &cdata;
+    msg.msg_controllen = sizeof(cdata);
+    
+    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+    
+    size_t controllen = 0;
+    
+    switch (o->send.local_addr.type) {
+        case BADDR_TYPE_IPV4: {
+#ifdef BADVPN_FREEBSD
+            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr)));
+            cmsg->cmsg_level = IPPROTO_IP;
+            cmsg->cmsg_type = IP_SENDSRCADDR;
+            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+            struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg);
+            addrinfo->s_addr = local_addr->ipv4;;
+            controllen += CMSG_SPACE(sizeof(struct in_addr));
+#else
+            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
+            cmsg->cmsg_level = IPPROTO_IP;
+            cmsg->cmsg_type = IP_PKTINFO;
+            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+            struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+            pktinfo->ipi_spec_dst.s_addr = o->send.local_addr.ipv4;
+            controllen += CMSG_SPACE(sizeof(struct in_pktinfo));
+#endif
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
+            cmsg->cmsg_level = IPPROTO_IPV6;
+            cmsg->cmsg_type = IPV6_PKTINFO;
+            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+            struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+            memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16);
+            controllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
+        } break;
+    }
+    
+    msg.msg_controllen = controllen;
+    
+    if (msg.msg_controllen == 0) {
+        msg.msg_control = NULL;
+    }
+    
+    // send
+    int bytes = sendmsg(o->fd, &msg, 0);
+    if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_WRITE;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    if (bytes < 0) {
+        BLog(BLOG_ERROR, "send failed");
+        report_error(o);
+        return;
+    }
+    
+    ASSERT(bytes >= 0)
+    ASSERT(bytes <= o->send.busy_data_len)
+    
+    if (bytes < o->send.busy_data_len) {
+        BLog(BLOG_ERROR, "send sent too little");
+    }
+    
+    // if recv wasn't started yet, start it
+    if (!o->recv.started) {
+        // set recv started
+        o->recv.started = 1;
+        
+        // continue receiving
+        if (o->recv.inited && o->recv.busy) {
+            BPending_Set(&o->recv.job);
+        }
+    }
+    
+    // set not busy
+    o->send.busy = 0;
+    
+    // done
+    PacketPassInterface_Done(&o->send.iface);
+}
+
+static void recv_job_handler (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->recv.inited)
+    ASSERT(o->recv.busy)
+    ASSERT(o->recv.started)
+    
+    // limit
+    if (!BReactorLimit_Increment(&o->recv.limit)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_READ;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    
+    struct sys_addr sysaddr;
+    
+    struct iovec iov;
+    iov.iov_base = o->recv.busy_data;
+    iov.iov_len = o->recv.mtu;
+    
+    union {
+#ifdef BADVPN_FREEBSD
+        char in[CMSG_SPACE(sizeof(struct in_addr))];
+#else
+        char in[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#endif
+        char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+    } cdata;
+    
+    struct msghdr msg;
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = &sysaddr.addr.generic;
+    msg.msg_namelen = sizeof(sysaddr.addr);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = &cdata;
+    msg.msg_controllen = sizeof(cdata);
+    
+    // recv
+    int bytes = recvmsg(o->fd, &msg, 0);
+    if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+        // wait for fd
+        o->wait_events |= BREACTOR_READ;
+        BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+        return;
+    }
+    if (bytes < 0) {
+        BLog(BLOG_ERROR, "recv failed");
+        report_error(o);
+        return;
+    }
+    
+    ASSERT(bytes >= 0)
+    ASSERT(bytes <= o->recv.mtu)
+    
+    // read returned address
+    sysaddr.len = msg.msg_namelen;
+    addr_sys_to_socket(&o->recv.remote_addr, sysaddr);
+    
+    // read returned local address
+    BIPAddr_InitInvalid(&o->recv.local_addr);
+    struct cmsghdr *cmsg;
+    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+#ifdef BADVPN_FREEBSD
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
+            struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg);
+            BIPAddr_InitIPv4(&o->recv.local_addr, addrinfo->s_addr);
+        }
+#else
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+            struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+            BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr);
+        }
+#endif
+        else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+            struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+            BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr);
+        }
+    }
+    
+    // set have addresses
+    o->recv.have_addrs = 1;
+    
+    // set not busy
+    o->recv.busy = 0;
+    
+    // done
+    PacketRecvInterface_Done(&o->recv.iface, bytes);
+}
+
+static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->send.inited)
+    ASSERT(!o->send.busy)
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->send.mtu)
+    
+    // remember data
+    o->send.busy_data = data;
+    o->send.busy_data_len = data_len;
+    
+    // set busy
+    o->send.busy = 1;
+    
+    // if have no addresses, wait
+    if (!o->send.have_addrs) {
+        return;
+    }
+    
+    // set job
+    BPending_Set(&o->send.job);
+}
+
+static void recv_if_handler_recv (BDatagram *o, uint8_t *data)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->recv.inited)
+    ASSERT(!o->recv.busy)
+    
+    // remember data
+    o->recv.busy_data = data;
+    
+    // set busy
+    o->recv.busy = 1;
+    
+    // if recv not started yet, wait
+    if (!o->recv.started) {
+        return;
+    }
+    
+    // set job
+    BPending_Set(&o->recv.job);
+}
+
+int BDatagram_AddressFamilySupported (int family)
+{
+    switch (family) {
+        case BADDR_TYPE_IPV4:
+        case BADDR_TYPE_IPV6:
+#ifdef BADVPN_LINUX
+        case BADDR_TYPE_PACKET:
+#endif
+            return 1;
+    }
+    
+    return 0;
+}
+
+int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user,
+                    BDatagram_handler handler)
+{
+    ASSERT(BDatagram_AddressFamilySupported(family))
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // init fd
+    if ((o->fd = socket(family_socket_to_sys(family), SOCK_DGRAM, 0)) < 0) {
+        BLog(BLOG_ERROR, "socket failed");
+        goto fail0;
+    }
+    
+    // set fd non-blocking
+    if (!badvpn_set_nonblocking(o->fd)) {
+        BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
+        goto fail1;
+    }
+    
+    // enable receiving pktinfo
+    set_pktinfo(o->fd, family);
+    
+    // init BFileDescriptor
+    BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o);
+    if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
+        BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+        goto fail1;
+    }
+    
+    // set no wait events
+    o->wait_events = 0;
+    
+    // init limits
+    BReactorLimit_Init(&o->send.limit, o->reactor, BDATAGRAM_SEND_LIMIT);
+    BReactorLimit_Init(&o->recv.limit, o->reactor, BDATAGRAM_RECV_LIMIT);
+    
+    // set have no send and recv addresses
+    o->send.have_addrs = 0;
+    o->recv.have_addrs = 0;
+    
+    // set recv not started
+    o->recv.started = 0;
+    
+    // set send and recv not inited
+    o->send.inited = 0;
+    o->recv.inited = 0;
+    
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    if (close(o->fd) < 0) {
+        BLog(BLOG_ERROR, "close failed");
+    }
+fail0:
+    return 0;
+}
+
+void BDatagram_Free (BDatagram *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
+    ASSERT(!o->recv.inited)
+    ASSERT(!o->send.inited)
+    
+    // free limits
+    BReactorLimit_Free(&o->recv.limit);
+    BReactorLimit_Free(&o->send.limit);
+    
+    // free BFileDescriptor
+    BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    
+    // free fd
+    if (close(o->fd) < 0) {
+        BLog(BLOG_ERROR, "close failed");
+    }
+}
+
+int BDatagram_Bind (BDatagram *o, BAddr addr)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(BDatagram_AddressFamilySupported(addr.type))
+    
+    // translate address
+    struct sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, addr);
+    
+    // bind
+    if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) {
+        BLog(BLOG_ERROR, "bind failed");
+        return 0;
+    }
+    
+    // if recv wasn't started yet, start it
+    if (!o->recv.started) {
+        // set recv started
+        o->recv.started = 1;
+        
+        // continue receiving
+        if (o->recv.inited && o->recv.busy) {
+            BPending_Set(&o->recv.job);
+        }
+    }
+    
+    return 1;
+}
+
+void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(BDatagram_AddressFamilySupported(remote_addr.type))
+    ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type))
+    
+    int had_send_addrs = o->send.have_addrs;
+    
+    // set addresses
+    o->send.remote_addr = remote_addr;
+    o->send.local_addr = local_addr;
+    
+    // set have addresses
+    o->send.have_addrs = 1;
+    
+    // start sending
+    if (!had_send_addrs && o->send.inited && o->send.busy) {
+        BPending_Set(&o->send.job);
+    }
+}
+
+int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    if (!o->recv.have_addrs) {
+        return 0;
+    }
+    
+    *remote_addr = o->recv.remote_addr;
+    *local_addr = o->recv.local_addr;
+    return 1;
+}
+
+int BDatagram_GetFd (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    return o->fd;
+}
+
+void BDatagram_SendAsync_Init (BDatagram *o, int mtu)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->send.inited)
+    ASSERT(mtu >= 0)
+    
+    // init arguments
+    o->send.mtu = mtu;
+    
+    // init interface
+    PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor));
+    
+    // init job
+    BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o);
+    
+    // set not busy
+    o->send.busy = 0;
+    
+    // set inited
+    o->send.inited = 1;
+}
+
+void BDatagram_SendAsync_Free (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    // update events
+    o->wait_events &= ~BREACTOR_WRITE;
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+    
+    // free job
+    BPending_Free(&o->send.job);
+    
+    // free interface
+    PacketPassInterface_Free(&o->send.iface);
+    
+    // set not inited
+    o->send.inited = 0;
+}
+
+PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    return &o->send.iface;
+}
+
+void BDatagram_RecvAsync_Init (BDatagram *o, int mtu)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->recv.inited)
+    ASSERT(mtu >= 0)
+    
+    // init arguments
+    o->recv.mtu = mtu;
+    
+    // init interface
+    PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor));
+    
+    // init job
+    BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o);
+    
+    // set not busy
+    o->recv.busy = 0;
+    
+    // set inited
+    o->recv.inited = 1;
+}
+
+void BDatagram_RecvAsync_Free (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    // update events
+    o->wait_events &= ~BREACTOR_READ;
+    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events);
+    
+    // free job
+    BPending_Free(&o->recv.job);
+    
+    // free interface
+    PacketRecvInterface_Free(&o->recv.iface);
+    
+    // set not inited
+    o->recv.inited = 0;
+}
+
+PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    return &o->recv.iface;
+}

+ 64 - 0
system/BDatagram_unix.h

@@ -0,0 +1,64 @@
+/**
+ * @file BDatagram_unix.h
+ * @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/debugerror.h>
+#include <base/DebugObject.h>
+
+#define BDATAGRAM_SEND_LIMIT 2
+#define BDATAGRAM_RECV_LIMIT 2
+
+struct BDatagram_s {
+    BReactor *reactor;
+    void *user;
+    BDatagram_handler handler;
+    int fd;
+    BFileDescriptor bfd;
+    int wait_events;
+    struct {
+        BReactorLimit limit;
+        int have_addrs;
+        BAddr remote_addr;
+        BIPAddr local_addr;
+        int inited;
+        int mtu;
+        PacketPassInterface iface;
+        BPending job;
+        int busy;
+        const uint8_t *busy_data;
+        int busy_data_len;
+    } send;
+    struct {
+        BReactorLimit limit;
+        int started;
+        int have_addrs;
+        BAddr remote_addr;
+        BIPAddr local_addr;
+        int inited;
+        int mtu;
+        PacketRecvInterface iface;
+        BPending job;
+        int busy;
+        uint8_t *busy_data;
+    } recv;
+    DebugError d_err;
+    DebugObject d_obj;
+};

+ 738 - 0
system/BDatagram_win.c

@@ -0,0 +1,738 @@
+/**
+ * @file BDatagram_win.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 <stdlib.h>
+
+#include <base/BLog.h>
+
+#include "BDatagram.h"
+
+#include <generated/blog_channel_BDatagram.h>
+
+static int family_socket_to_sys (int family);
+static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr);
+static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr);
+static void set_pktinfo (SOCKET sock, int family);
+static void report_error (BDatagram *o);
+static void datagram_abort (BDatagram *o);
+static void start_send (BDatagram *o);
+static void start_recv (BDatagram *o);
+static void send_job_handler (BDatagram *o);
+static void recv_job_handler (BDatagram *o);
+static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len);
+static void recv_if_handler_recv (BDatagram *o, uint8_t *data);
+static void send_olap_handler (BDatagram *o, int event, DWORD bytes);
+static void recv_olap_handler (BDatagram *o, int event, DWORD bytes);
+
+static int family_socket_to_sys (int family)
+{
+    switch (family) {
+        case BADDR_TYPE_IPV4:
+            return AF_INET;
+        case BADDR_TYPE_IPV6:
+            return AF_INET6;
+    }
+    
+    ASSERT(0);
+    return 0;
+}
+
+static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr)
+{
+    switch (addr.type) {
+        case BADDR_TYPE_IPV4: {
+            out->len = sizeof(out->addr.ipv4);
+            memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
+            out->addr.ipv4.sin_family = AF_INET;
+            out->addr.ipv4.sin_port = addr.ipv4.port;
+            out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip;
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            out->len = sizeof(out->addr.ipv6);
+            memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
+            out->addr.ipv6.sin6_family = AF_INET6;
+            out->addr.ipv6.sin6_port = addr.ipv6.port;
+            out->addr.ipv6.sin6_flowinfo = 0;
+            memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16);
+            out->addr.ipv6.sin6_scope_id = 0;
+        } break;
+        
+        default: ASSERT(0);
+    }
+}
+
+static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr)
+{
+    switch (addr.addr.generic.sa_family) {
+        case AF_INET: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in))
+            BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port);
+        } break;
+        
+        case AF_INET6: {
+            ASSERT(addr.len == sizeof(struct sockaddr_in6))
+            BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port);
+        } break;
+        
+        default: {
+            BAddr_InitNone(out);
+        } break;
+    }
+}
+
+static void set_pktinfo (SOCKET sock, int family)
+{
+    DWORD opt = 1;
+    
+    switch (family) {
+        case BADDR_TYPE_IPV4: {
+            if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt)) < 0) {
+                BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed");
+            }
+        } break;
+        
+        case BADDR_TYPE_IPV6: {
+            if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&opt, sizeof(opt)) < 0) {
+                BLog(BLOG_ERROR, "setsockopt(IPV6_PKTINFO) failed");
+            }
+        } break;
+    }
+}
+
+static void report_error (BDatagram *o)
+{
+    DebugError_AssertNoError(&o->d_err);
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR));
+    return;
+}
+
+static void datagram_abort (BDatagram *o)
+{
+    ASSERT(!o->aborted)
+    
+    // cancel I/O
+    if ((o->recv.inited && o->recv.data_have && o->recv.data_busy) || (o->send.inited && o->send.data_len >= 0 && o->send.data_busy)) {
+        if (!CancelIo((HANDLE)o->sock)) {
+            BLog(BLOG_ERROR, "CancelIo failed");
+        }
+    }
+    
+    // close socket
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+    
+    // wait for receiving to complete
+    if (o->recv.inited && o->recv.data_have && o->recv.data_busy) {
+        BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL);
+    }
+    
+    // wait for sending to complete
+    if (o->send.inited && o->send.data_len >= 0 && o->send.data_busy) {
+        BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL);
+    }
+    
+    // free recv olap
+    BReactorIOCPOverlapped_Free(&o->recv.olap);
+    
+    // free send olap
+    BReactorIOCPOverlapped_Free(&o->send.olap);
+    
+    // set aborted
+    o->aborted = 1;
+}
+
+static void start_send (BDatagram *o)
+{
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->send.inited)
+    ASSERT(o->send.data_len >= 0)
+    ASSERT(!o->send.data_busy)
+    ASSERT(o->send.have_addrs)
+    
+    // convert destination address
+    addr_socket_to_sys(&o->send.sysaddr, o->send.remote_addr);
+    
+    WSABUF buf;
+    buf.buf = o->send.data;
+    buf.len = (o->send.data_len > ULONG_MAX ? ULONG_MAX : o->send.data_len);
+    
+    memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap));
+    
+    if (o->fnWSASendMsg) {
+        o->send.msg.name = &o->send.sysaddr.addr.generic;
+        o->send.msg.namelen = o->send.sysaddr.len;
+        o->send.msg.lpBuffers = &buf;
+        o->send.msg.dwBufferCount = 1;
+        o->send.msg.Control.buf = (char *)&o->send.cdata;
+        o->send.msg.Control.len = sizeof(o->send.cdata);
+        o->send.msg.dwFlags = 0;
+        
+        int sum = 0;
+        
+        WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->send.msg);
+        
+        switch (o->send.local_addr.type) {
+            case BADDR_TYPE_IPV4: {
+                memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
+                cmsg->cmsg_level = IPPROTO_IP;
+                cmsg->cmsg_type = IP_PKTINFO;
+                cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
+                struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg);
+                pktinfo->ipi_addr.s_addr = o->send.local_addr.ipv4;
+                sum += WSA_CMSG_SPACE(sizeof(struct in_pktinfo));
+            } break;
+            case BADDR_TYPE_IPV6: {
+                memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)));
+                cmsg->cmsg_level = IPPROTO_IPV6;
+                cmsg->cmsg_type = IPV6_PKTINFO;
+                cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in6_pktinfo));
+                struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg);
+                memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16);
+                sum += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo));
+            } break;
+        }
+        
+        o->send.msg.Control.len = sum;
+        
+        if (o->send.msg.Control.len == 0) {
+            o->send.msg.Control.buf = NULL;
+        }
+        
+        // send
+        int res = o->fnWSASendMsg(o->sock, &o->send.msg, 0, NULL, &o->send.olap.olap, NULL);
+        if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
+            BLog(BLOG_ERROR, "WSASendMsg failed (%d)", WSAGetLastError());
+            report_error(o);
+            return;
+        }
+    } else {
+        // send
+        int res = WSASendTo(o->sock, &buf, 1, NULL, 0, &o->send.sysaddr.addr.generic, o->send.sysaddr.len, &o->send.olap.olap, NULL);
+        if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
+            BLog(BLOG_ERROR, "WSASendTo failed (%d)", WSAGetLastError());
+            report_error(o);
+            return;
+        }
+    }
+    
+    // set busy
+    o->send.data_busy = 1;
+}
+
+static void start_recv (BDatagram *o)
+{
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->recv.inited)
+    ASSERT(o->recv.data_have)
+    ASSERT(!o->recv.data_busy)
+    ASSERT(o->recv.started)
+    
+    WSABUF buf;
+    buf.buf = o->recv.data;
+    buf.len = (o->recv.mtu > ULONG_MAX ? ULONG_MAX : o->recv.mtu);
+    
+    memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap));
+    
+    if (o->fnWSARecvMsg) {
+        o->recv.msg.name = &o->recv.sysaddr.addr.generic;
+        o->recv.msg.namelen = sizeof(o->recv.sysaddr.addr);
+        o->recv.msg.lpBuffers = &buf;
+        o->recv.msg.dwBufferCount = 1;
+        o->recv.msg.Control.buf = (char *)&o->recv.cdata;
+        o->recv.msg.Control.len = sizeof(o->recv.cdata);
+        o->recv.msg.dwFlags = 0;
+        
+        // recv
+        int res = o->fnWSARecvMsg(o->sock, &o->recv.msg, NULL, &o->recv.olap.olap, NULL);
+        if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
+            BLog(BLOG_ERROR, "WSARecvMsg failed (%d)", WSAGetLastError());
+            report_error(o);
+            return;
+        }
+    } else {
+        o->recv.sysaddr.len = sizeof(o->recv.sysaddr.addr);
+        
+        // recv
+        DWORD flags = 0;
+        int res = WSARecvFrom(o->sock, &buf, 1, NULL, &flags, &o->recv.sysaddr.addr.generic, &o->recv.sysaddr.len, &o->recv.olap.olap, NULL);
+        if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
+            BLog(BLOG_ERROR, "WSARecvFrom failed (%d)", WSAGetLastError());
+            report_error(o);
+            return;
+        }
+    }
+    
+    // set busy
+    o->recv.data_busy = 1;
+}
+
+static void send_job_handler (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->send.inited)
+    ASSERT(o->send.data_len >= 0)
+    ASSERT(!o->send.data_busy)
+    ASSERT(o->send.have_addrs)
+    
+    // send
+    start_send(o);
+    return;
+}
+
+static void recv_job_handler (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->recv.inited)
+    ASSERT(o->recv.data_have)
+    ASSERT(!o->recv.data_busy)
+    ASSERT(o->recv.started)
+    
+    // recv
+    start_recv(o);
+    return;
+}
+
+static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->send.inited)
+    ASSERT(o->send.data_len == -1)
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->send.mtu)
+    
+    // remember data
+    o->send.data = data;
+    o->send.data_len = data_len;
+    o->send.data_busy = 0;
+    
+    // if have no addresses, wait
+    if (!o->send.have_addrs) {
+        return;
+    }
+    
+    // send
+    start_send(o);
+    return;
+}
+
+static void recv_if_handler_recv (BDatagram *o, uint8_t *data)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->recv.inited)
+    ASSERT(!o->recv.data_have)
+    
+    // remember data
+    o->recv.data = data;
+    o->recv.data_have = 1;
+    o->recv.data_busy = 0;
+    
+    // if recv not started yet, wait
+    if (!o->recv.started) {
+        return;
+    }
+    
+    // recv
+    start_recv(o);
+    return;
+}
+
+static void send_olap_handler (BDatagram *o, int event, DWORD bytes)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->send.inited)
+    ASSERT(o->send.data_len >= 0)
+    ASSERT(o->send.data_busy)
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
+    
+    // set not busy
+    o->send.data_busy = 0;
+    
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "sending failed");
+        report_error(o);
+        return;
+    }
+    
+    ASSERT(bytes >= 0)
+    ASSERT(bytes <= o->send.data_len)
+    
+    if (bytes < o->send.data_len) {
+        BLog(BLOG_ERROR, "sent too little");
+    }
+    
+    // if recv wasn't started yet, start it
+    if (!o->recv.started) {
+        // set recv started
+        o->recv.started = 1;
+        
+        // continue receiving
+        if (o->recv.inited && o->recv.data_have) {
+            ASSERT(!o->recv.data_busy)
+            
+            BPending_Set(&o->recv.job);
+        }
+    }
+    
+    // set no data
+    o->send.data_len = -1;
+    
+    // done
+    PacketPassInterface_Done(&o->send.iface);
+}
+
+static void recv_olap_handler (BDatagram *o, int event, DWORD bytes)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(o->recv.inited)
+    ASSERT(o->recv.data_have)
+    ASSERT(o->recv.data_busy)
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
+    
+    // set not busy
+    o->recv.data_busy = 0;
+    
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "receiving failed");
+        report_error(o);
+        return;
+    }
+    
+    ASSERT(bytes >= 0)
+    ASSERT(bytes <= o->recv.mtu)
+    
+    if (o->fnWSARecvMsg) {
+        o->recv.sysaddr.len = o->recv.msg.namelen;
+    }
+    
+    // read remote address
+    addr_sys_to_socket(&o->recv.remote_addr, o->recv.sysaddr);
+    
+    // read local address
+    BIPAddr_InitInvalid(&o->recv.local_addr);
+    if (o->fnWSARecvMsg) {
+        for (WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->recv.msg); cmsg; cmsg = WSA_CMSG_NXTHDR(&o->recv.msg, cmsg)) {
+            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+                struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg);
+                BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr);
+            }
+            else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+                struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg);
+                BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr);
+            }
+        }
+    }
+    
+    // set have addresses
+    o->recv.have_addrs = 1;
+    
+    // set no data
+    o->recv.data_have = 0;
+    
+    // done
+    PacketRecvInterface_Done(&o->recv.iface, bytes);
+}
+
+int BDatagram_AddressFamilySupported (int family)
+{
+    return (family == BADDR_TYPE_IPV4 || family == BADDR_TYPE_IPV6);
+}
+
+int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user,
+                    BDatagram_handler handler)
+{
+    ASSERT(BDatagram_AddressFamilySupported(family))
+    ASSERT(handler)
+    BNetwork_Assert();
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // init socket
+    if ((o->sock = WSASocket(family_socket_to_sys(family), SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
+        BLog(BLOG_ERROR, "WSASocket failed");
+        goto fail0;
+    }
+    
+    GUID guid;
+    DWORD out_bytes;
+    
+    // obtain WSASendMsg
+    guid = (GUID)WSAID_WSASENDMSG;
+    if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnWSASendMsg, sizeof(o->fnWSASendMsg), &out_bytes, NULL, NULL) != 0) {
+        BLog(BLOG_WARNING, "failed to obtain WSASendMsg");
+        o->fnWSASendMsg = NULL;
+    }
+    
+    // obtain WSARecvMsg
+    guid = (GUID)WSAID_WSARECVMSG;
+    if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnWSARecvMsg, sizeof(o->fnWSARecvMsg), &out_bytes, NULL, NULL) != 0) {
+        BLog(BLOG_ERROR, "failed to obtain WSARecvMsg");
+        o->fnWSARecvMsg = NULL;
+    }
+    
+    // associate with IOCP
+    if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) {
+        BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
+        goto fail1;
+    }
+    
+    // enable receiving pktinfo
+    set_pktinfo(o->sock, family);
+    
+    // set not aborted
+    o->aborted = 0;
+    
+    // init send olap
+    BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)send_olap_handler);
+    
+    // set have no send addrs
+    o->send.have_addrs = 0;
+    
+    // set send not inited
+    o->send.inited = 0;
+    
+    // init recv olap
+    BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler);
+    
+    // set recv not started
+    o->recv.started = 0;
+    
+    // set have no recv addrs
+    o->recv.have_addrs = 0;
+    
+    // set recv not inited
+    o->recv.inited = 0;
+    
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    if (closesocket(o->sock) == SOCKET_ERROR) {
+        BLog(BLOG_ERROR, "closesocket failed");
+    }
+fail0:
+    return 0;
+}
+
+void BDatagram_Free (BDatagram *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
+    ASSERT(!o->recv.inited)
+    ASSERT(!o->send.inited)
+    
+    if (!o->aborted) {
+        datagram_abort(o);
+    }
+}
+
+int BDatagram_Bind (BDatagram *o, BAddr addr)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(BDatagram_AddressFamilySupported(addr.type))
+    
+    // translate address
+    struct BDatagram_sys_addr sysaddr;
+    addr_socket_to_sys(&sysaddr, addr);
+    
+    // bind
+    if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) {
+        BLog(BLOG_ERROR, "bind failed");
+        return 0;
+    }
+    
+    // if recv wasn't started yet, start it
+    if (!o->recv.started) {
+        // set recv started
+        o->recv.started = 1;
+        
+        // continue receiving
+        if (o->recv.inited && o->recv.data_have) {
+            ASSERT(!o->recv.data_busy)
+            
+            BPending_Set(&o->recv.job);
+        }
+    }
+    
+    return 1;
+}
+
+void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(BDatagram_AddressFamilySupported(remote_addr.type))
+    ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type))
+    
+    // set addresses
+    o->send.remote_addr = remote_addr;
+    o->send.local_addr = local_addr;
+    
+    // set have addresses
+    o->send.have_addrs = 1;
+    
+    // start sending
+    if (o->send.inited && o->send.data_len >= 0 && !o->send.data_busy) {
+        BPending_Set(&o->send.job);
+    }
+}
+
+int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    if (!o->recv.have_addrs) {
+        return 0;
+    }
+    
+    *remote_addr = o->recv.remote_addr;
+    *local_addr = o->recv.local_addr;
+    return 1;
+}
+
+void BDatagram_SendAsync_Init (BDatagram *o, int mtu)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(!o->send.inited)
+    ASSERT(mtu >= 0)
+    
+    // init arguments
+    o->send.mtu = mtu;
+    
+    // init interface
+    PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor));
+    
+    // init job
+    BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o);
+    
+    // set have no data
+    o->send.data_len = -1;
+    
+    // set inited
+    o->send.inited = 1;
+}
+
+void BDatagram_SendAsync_Free (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    // abort if busy
+    if (o->send.data_len >= 0 && o->send.data_busy && !o->aborted) {
+        datagram_abort(o);
+    }
+    
+    // free job
+    BPending_Free(&o->send.job);
+    
+    // free interface
+    PacketPassInterface_Free(&o->send.iface);
+    
+    // set not inited
+    o->send.inited = 0;
+}
+
+PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send.inited)
+    
+    return &o->send.iface;
+}
+
+void BDatagram_RecvAsync_Init (BDatagram *o, int mtu)
+{
+    DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(!o->aborted)
+    ASSERT(!o->recv.inited)
+    ASSERT(mtu >= 0)
+    
+    // init arguments
+    o->recv.mtu = mtu;
+    
+    // init interface
+    PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor));
+    
+    // init job
+    BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o);
+    
+    // set have no data
+    o->recv.data_have = 0;
+    
+    // set inited
+    o->recv.inited = 1;
+}
+
+void BDatagram_RecvAsync_Free (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    // abort if busy
+    if (o->recv.data_have && o->recv.data_busy && !o->aborted) {
+        datagram_abort(o);
+    }
+    
+    // free job
+    BPending_Free(&o->recv.job);
+    
+    // free interface
+    PacketRecvInterface_Free(&o->recv.iface);
+    
+    // set not inited
+    o->recv.inited = 0;
+}
+
+PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->recv.inited)
+    
+    return &o->recv.iface;
+}

+ 92 - 0
system/BDatagram_win.h

@@ -0,0 +1,92 @@
+/**
+ * @file BDatagram_win.h
+ * @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 <windows.h>
+#include <winsock2.h>
+#ifdef BADVPN_USE_SHIPPED_MSWSOCK
+#    include <misc/mswsock.h>
+#else
+#    include <mswsock.h>
+#endif
+
+#include <misc/debugerror.h>
+#include <base/DebugObject.h>
+
+struct BDatagram_sys_addr {
+    int len;
+    union {
+        struct sockaddr generic;
+        struct sockaddr_in ipv4;
+        struct sockaddr_in6 ipv6;
+    } addr;
+};
+
+struct BDatagram_s {
+    BReactor *reactor;
+    void *user;
+    BDatagram_handler handler;
+    SOCKET sock;
+    LPFN_WSASENDMSG fnWSASendMsg;
+    LPFN_WSARECVMSG fnWSARecvMsg;
+    int aborted;
+    struct {
+        BReactorIOCPOverlapped olap;
+        int have_addrs;
+        BAddr remote_addr;
+        BIPAddr local_addr;
+        int inited;
+        int mtu;
+        PacketPassInterface iface;
+        BPending job;
+        int data_len;
+        uint8_t *data;
+        int data_busy;
+        struct BDatagram_sys_addr sysaddr;
+        union {
+            char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
+            char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))];
+        } cdata;
+        WSAMSG msg;
+    } send;
+    struct {
+        BReactorIOCPOverlapped olap;
+        int started;
+        int have_addrs;
+        BAddr remote_addr;
+        BIPAddr local_addr;
+        int inited;
+        int mtu;
+        PacketRecvInterface iface;
+        BPending job;
+        int data_have;
+        uint8_t *data;
+        int data_busy;
+        struct BDatagram_sys_addr sysaddr;
+        union {
+            char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
+            char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))];
+        } cdata;
+        WSAMSG msg;
+    } recv;
+    DebugError d_err;
+    DebugObject d_obj;
+};

+ 88 - 0
system/BNetwork.c

@@ -0,0 +1,88 @@
+/**
+ * @file BNetwork.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.
+ */
+
+#ifdef BADVPN_USE_WINAPI
+#include <windows.h>
+#include <winsock2.h>
+#include <mswsock.h>
+#else
+#include <string.h>
+#include <signal.h>
+#endif
+
+#include <misc/debug.h>
+#include <base/BLog.h>
+
+#include <system/BNetwork.h>
+
+#include <generated/blog_channel_BNetwork.h>
+
+int bnetwork_initialized = 0;
+
+int BNetwork_GlobalInit (void)
+{
+    ASSERT(!bnetwork_initialized)
+    
+#ifdef BADVPN_USE_WINAPI
+    
+    WORD requested = MAKEWORD(2, 2);
+    WSADATA wsadata;
+    if (WSAStartup(requested, &wsadata) != 0) {
+        BLog(BLOG_ERROR, "WSAStartup failed");
+        goto fail0;
+    }
+    if (wsadata.wVersion != requested) {
+        BLog(BLOG_ERROR, "WSAStartup returned wrong version");
+        goto fail1;
+    }
+    
+#else
+    
+    struct sigaction act;
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = SIG_IGN;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = 0;
+    if (sigaction(SIGPIPE, &act, NULL) < 0) {
+        BLog(BLOG_ERROR, "sigaction failed");
+        goto fail0;
+    }
+    
+#endif
+    
+    bnetwork_initialized = 1;
+    
+    return 1;
+    
+#ifdef BADVPN_USE_WINAPI
+fail1:
+    WSACleanup();
+#endif
+    
+fail0:
+    return 0;
+}
+
+void BNetwork_Assert (void)
+{
+    ASSERT(bnetwork_initialized)
+}

+ 29 - 0
system/BNetwork.h

@@ -0,0 +1,29 @@
+/**
+ * @file BNetwork.h
+ * @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.
+ */
+
+#ifndef BADVPN_SYSTEM_BNETWORK_H
+#define BADVPN_SYSTEM_BNETWORK_H
+
+int BNetwork_GlobalInit (void);
+void BNetwork_Assert (void);
+
+#endif

+ 166 - 116
system/BReactor.c

@@ -126,6 +126,26 @@ static void move_first_timers (BReactor *bsys)
     }
 }
 
+#ifdef BADVPN_USE_WINAPI
+
+static void set_iocp_ready (BReactorIOCPOverlapped *olap, int succeeded, DWORD bytes)
+{
+    BReactor *reactor = olap->reactor;
+    ASSERT(!olap->is_ready)
+    
+    // set parameters
+    olap->ready_succeeded = succeeded;
+    olap->ready_bytes = bytes;
+    
+    // insert to IOCP ready list
+    LinkedList1_Append(&reactor->iocp_ready_list, &olap->ready_list_node);
+    
+    // set ready
+    olap->is_ready = 1;
+}
+
+#endif
+
 #ifdef BADVPN_USE_EPOLL
 
 static void set_epoll_fd_pointers (BReactor *bsys)
@@ -234,7 +254,7 @@ static void wait_for_events (BReactor *bsys)
     ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs))
     ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list))
     #ifdef BADVPN_USE_WINAPI
-    ASSERT(!bsys->returned_object)
+    ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list))
     #endif
     #ifdef BADVPN_USE_EPOLL
     ASSERT(bsys->epoll_results_pos == bsys->epoll_results_num)
@@ -307,20 +327,24 @@ static void wait_for_events (BReactor *bsys)
             }
         }
         
-        BLog(BLOG_DEBUG, "Calling WaitForMultipleObjects on %d handles", bsys->enabled_num);
+        DWORD bytes = 0;
+        ULONG_PTR key;
+        BReactorIOCPOverlapped *olap = NULL;
+        BOOL res = GetQueuedCompletionStatus(bsys->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, (have_timeout ? timeout_rel_trunc : INFINITE));
         
-        DWORD waitres = WaitForMultipleObjects(bsys->enabled_num, bsys->enabled_handles, FALSE, (have_timeout ? timeout_rel_trunc : INFINITE));
-        ASSERT_FORCE(waitres != WAIT_FAILED)
-        ASSERT_FORCE(!(waitres == WAIT_TIMEOUT) || have_timeout)
-        ASSERT_FORCE(!(waitres != WAIT_TIMEOUT) || (waitres >= WAIT_OBJECT_0 && waitres < WAIT_OBJECT_0 + bsys->enabled_num))
+        ASSERT_FORCE(olap || have_timeout)
         
-        if (waitres != WAIT_TIMEOUT || timeout_rel_trunc == timeout_rel) {
-            if (waitres != WAIT_TIMEOUT) {
-                int handle_index = waitres - WAIT_OBJECT_0;
-                BLog(BLOG_DEBUG, "WaitForMultipleObjects returned handle %d", handle_index);
-                bsys->returned_object = bsys->enabled_objects[handle_index];
+        if (olap || timeout_rel_trunc == timeout_rel) {
+            if (olap) {
+                BLog(BLOG_DEBUG, "GetQueuedCompletionStatus returned event");
+                
+                DebugObject_Access(&olap->d_obj);
+                ASSERT(olap->reactor == bsys)
+                ASSERT(!olap->is_ready)
+                
+                set_iocp_ready(olap, (res == TRUE), bytes);
             } else {
-                BLog(BLOG_DEBUG, "WaitForMultipleObjects timed out");
+                BLog(BLOG_DEBUG, "GetQueuedCompletionStatus timed out");
                 move_first_timers(bsys);
             }
             break;
@@ -502,17 +526,7 @@ static void wait_for_events (BReactor *bsys)
     }
 }
 
-#ifdef BADVPN_USE_WINAPI
-
-void BHandle_Init (BHandle *bh, HANDLE handle, BHandle_handler handler, void *user)
-{
-    bh->h = handle;
-    bh->handler = handler;
-    bh->user = user;
-    bh->active = 0;
-}
-
-#else
+#ifndef BADVPN_USE_WINAPI
 
 void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user)
 {
@@ -559,9 +573,17 @@ int BReactor_Init (BReactor *bsys)
     
     #ifdef BADVPN_USE_WINAPI
     
-    bsys->num_handles = 0;
-    bsys->enabled_num = 0;
-    bsys->returned_object = NULL;
+    // init IOCP list
+    LinkedList1_Init(&bsys->iocp_list);
+    
+    // init IOCP handle
+    if (!(bsys->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1))) {
+        BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
+        goto fail0;
+    }
+    
+    // init IOCP ready list
+    LinkedList1_Init(&bsys->iocp_ready_list);
     
     #endif
     
@@ -640,15 +662,26 @@ fail0:
 
 void BReactor_Free (BReactor *bsys)
 {
+    DebugObject_Access(&bsys->d_obj);
+    
+    #ifdef BADVPN_USE_WINAPI
+    while (!LinkedList1_IsEmpty(&bsys->iocp_list)) {
+        BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetLast(&bsys->iocp_list), BReactorIOCPOverlapped, iocp_list_node);
+        ASSERT(olap->reactor == bsys)
+        olap->handler(olap->user, BREACTOR_IOCP_EVENT_EXITING, 0);
+    }
+    #endif
+    
     // {pending group has no BPending objects}
     ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs))
     ASSERT(!BHeap_GetFirst(&bsys->timers_heap))
     ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list))
     ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list))
+    DebugObject_Free(&bsys->d_obj);
     #ifdef BADVPN_USE_WINAPI
-    ASSERT(bsys->num_handles == 0)
+    ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list))
+    ASSERT(LinkedList1_IsEmpty(&bsys->iocp_list))
     #endif
-    DebugObject_Free(&bsys->d_obj);
     #ifndef BADVPN_USE_WINAPI
     DebugCounter_Free(&bsys->d_fds_counter);
     #endif
@@ -663,6 +696,13 @@ void BReactor_Free (BReactor *bsys)
     
     BLog(BLOG_DEBUG, "Reactor freeing");
     
+    #ifdef BADVPN_USE_WINAPI
+    
+    // close IOCP handle
+    ASSERT_FORCE(CloseHandle(bsys->iocp_handle))
+    
+    #endif
+    
     #ifdef BADVPN_USE_EPOLL
     
     // close epoll fd
@@ -721,18 +761,21 @@ int BReactor_Exec (BReactor *bsys)
         
         #ifdef BADVPN_USE_WINAPI
         
-        // dispatch handle
-        if (bsys->returned_object) {
-            BHandle *bh = bsys->returned_object;
-            bsys->returned_object = NULL;
-            ASSERT(bh->active)
-            ASSERT(bh->position >= 0 && bh->position < bsys->enabled_num)
-            ASSERT(bh == bsys->enabled_objects[bh->position])
-            ASSERT(bh->h == bsys->enabled_handles[bh->position])
+        if (!LinkedList1_IsEmpty(&bsys->iocp_ready_list)) {
+            BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetFirst(&bsys->iocp_ready_list), BReactorIOCPOverlapped, ready_list_node);
+            ASSERT(olap->is_ready)
+            ASSERT(olap->handler)
+            
+            // remove from ready list
+            LinkedList1_Remove(&bsys->iocp_ready_list, &olap->ready_list_node);
+            
+            // set not ready
+            olap->is_ready = 0;
+            
+            int event = (olap->ready_succeeded ? BREACTOR_IOCP_EVENT_SUCCEEDED : BREACTOR_IOCP_EVENT_FAILED);
             
             // call handler
-            BLog(BLOG_DEBUG, "Dispatching handle");
-            bh->handler(bh->user);
+            olap->handler(olap->user, event, olap->ready_bytes);
             continue;
         }
         
@@ -972,84 +1015,7 @@ int BReactor_Synchronize (BReactor *bsys, BPending *ref)
     return 0;
 }
 
-#ifdef BADVPN_USE_WINAPI
-
-int BReactor_AddHandle (BReactor *bsys, BHandle *bh)
-{
-    ASSERT(!bh->active)
-
-    if (bsys->num_handles >= BSYSTEM_MAX_HANDLES) {
-        return 0;
-    }
-
-    bh->active = 1;
-    bh->position = -1;
-
-    bsys->num_handles++;
-
-    return 1;
-}
-
-void BReactor_RemoveHandle (BReactor *bsys, BHandle *bh)
-{
-    ASSERT(bh->active)
-
-    if (bh->position >= 0) {
-        BReactor_DisableHandle(bsys, bh);
-    }
-
-    bh->active = 0;
-
-    ASSERT(bsys->num_handles > 0)
-    bsys->num_handles--;
-}
-
-void BReactor_EnableHandle (BReactor *bsys, BHandle *bh)
-{
-    ASSERT(bh->active)
-    ASSERT(bh->position == -1)
-
-    ASSERT(bsys->enabled_num < BSYSTEM_MAX_HANDLES)
-    bsys->enabled_handles[bsys->enabled_num] = bh->h;
-    bsys->enabled_objects[bsys->enabled_num] = bh;
-    bh->position = bsys->enabled_num;
-    bsys->enabled_num++;
-}
-
-void BReactor_DisableHandle (BReactor *bsys, BHandle *bh)
-{
-    ASSERT(bh->active)
-    ASSERT(bh->position >= 0)
-
-    ASSERT(bh->position < bsys->enabled_num)
-    ASSERT(bh == bsys->enabled_objects[bh->position])
-    ASSERT(bh->h == bsys->enabled_handles[bh->position])
-
-    // if there are more handles after this one, move the last
-    // one into its position
-    if (bh->position < bsys->enabled_num - 1) {
-        int move_position = bsys->enabled_num - 1;
-        BHandle *move_handle = bsys->enabled_objects[move_position];
-
-        ASSERT(move_handle->active)
-        ASSERT(move_handle->position == move_position)
-        ASSERT(move_handle->h == bsys->enabled_handles[move_position])
-
-        bsys->enabled_handles[bh->position] = move_handle->h;
-        bsys->enabled_objects[bh->position] = move_handle;
-        move_handle->position = bh->position;
-    }
-
-    bh->position = -1;
-    bsys->enabled_num--;
-
-    // make sure the handler will not be called
-    if (bsys->returned_object == bh) {
-        bsys->returned_object = NULL;
-    }
-}
-
-#else
+#ifndef BADVPN_USE_WINAPI
 
 int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
 {
@@ -1311,3 +1277,87 @@ void BReactorKEvent_Free (BReactorKEvent *o)
 }
 
 #endif
+
+#ifdef BADVPN_USE_WINAPI
+
+HANDLE BReactor_GetIOCPHandle (BReactor *reactor)
+{
+    DebugObject_Access(&reactor->d_obj);
+    
+    return reactor->iocp_handle;
+}
+
+void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler)
+{
+    DebugObject_Access(&reactor->d_obj);
+    
+    // init arguments
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // zero overlapped
+    memset(&o->olap, 0, sizeof(o->olap));
+    
+    // append to IOCP list
+    LinkedList1_Append(&reactor->iocp_list, &o->iocp_list_node);
+    
+    // set not ready
+    o->is_ready = 0;
+    
+    DebugObject_Init(&o->d_obj);
+}
+
+void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o)
+{
+    BReactor *reactor = o->reactor;
+    DebugObject_Free(&o->d_obj);
+    
+    // remove from IOCP ready list
+    if (o->is_ready) {
+        LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node);
+    }
+    
+    // remove from IOCP list
+    LinkedList1_Remove(&reactor->iocp_list, &o->iocp_list_node);
+}
+
+void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes)
+{
+    BReactor *reactor = o->reactor;
+    DebugObject_Access(&o->d_obj);
+    
+    // wait for IOCP events until we get an event for this olap
+    while (!o->is_ready) {
+        DWORD bytes = 0;
+        ULONG_PTR key;
+        BReactorIOCPOverlapped *olap = NULL;
+        BOOL res = GetQueuedCompletionStatus(reactor->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, INFINITE);
+        
+        ASSERT_FORCE(olap)
+        DebugObject_Access(&olap->d_obj);
+        ASSERT(olap->reactor == reactor)
+        
+        // regular I/O should be done synchronously, so we shoudln't ever get a second completion before an
+        // existing one is dispatched. If however PostQueuedCompletionStatus is being used to signal events,
+        // just discard any excess events.
+        if (!olap->is_ready) {
+            set_iocp_ready(olap, (res == TRUE), bytes);
+        }
+    }
+    
+    // remove from IOCP ready list
+    LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node);
+    
+    // set not ready
+    o->is_ready = 0;
+    
+    if (out_succeeded) {
+        *out_succeeded = o->ready_succeeded;
+    }
+    if (out_bytes) {
+        *out_bytes = o->ready_bytes;
+    }
+}
+
+#endif

+ 34 - 85
system/BReactor.h

@@ -109,42 +109,7 @@ void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user
  */
 int BTimer_IsRunning (BTimer *bt);
 
-#ifdef BADVPN_USE_WINAPI
-
-/**
- * Handler function invoked by the reactor when the handle is signalled
- * The handle object is in active state, is being called from within
- * the associated reactor, and is in monitored state.
- *
- * @param user value passed to {@link BHandle_Init}
- */
-typedef void (*BHandle_handler) (void *user);
-
-struct BHandle_t;
-
-/**
- * Windows handle object used with {@link BReactor}.
- */
-typedef struct BHandle_t {
-    HANDLE h;
-    BHandle_handler handler;
-    void *user;
-    int active;
-    int position;
-} BHandle;
-
-/**
- * Initializes the handle object.
- * The handle object is initialized in not active state.
- *
- * @param bh the object
- * @param handle underlying Windows handle
- * @param handler handler function invoked when the handle is signalled
- * @param user value to pass to the handler function
- */
-void BHandle_Init (BHandle *bh, HANDLE handle, BHandle_handler handler, void *user);
-
-#else
+#ifndef BADVPN_USE_WINAPI
 
 struct BFileDescriptor_t;
 
@@ -229,11 +194,9 @@ typedef struct {
     LinkedList1 active_limits_list;
     
     #ifdef BADVPN_USE_WINAPI
-    int num_handles; // number of user handles
-    int enabled_num; // number of user handles in the enabled array
-    HANDLE enabled_handles[BSYSTEM_MAX_HANDLES]; // enabled user handles
-    BHandle *enabled_objects[BSYSTEM_MAX_HANDLES]; // objects corresponding to enabled handles
-    BHandle *returned_object;
+    LinkedList1 iocp_list;
+    HANDLE iocp_handle;
+    LinkedList1 iocp_ready_list;
     #endif
     
     #ifdef BADVPN_USE_EPOLL
@@ -393,50 +356,7 @@ BPendingGroup * BReactor_PendingGroup (BReactor *bsys);
  */
 int BReactor_Synchronize (BReactor *bsys, BPending *ref);
 
-#ifdef BADVPN_USE_WINAPI
-
-/**
- * Registers a Windows handle to be monitored.
- *
- * @param bsys the object
- * @param bh handle object. Must have been initialized with {@link BHandle_Init}.
- *           Must be in not active state. On success, the handle object enters
- *           active state, associated with this reactor, and is initialized
- *           to not monitored state.
- * @return 1 on success, 0 on failure
- */
-int BReactor_AddHandle (BReactor *bsys, BHandle *bh) WARN_UNUSED;
-
-/**
- * Unregisters a Windows handle.
- *
- * @param bsys the object
- * @param bh handle object. Must be in active state, associated with this reactor.
- *           The handle object enters not active state.
- */
-void BReactor_RemoveHandle (BReactor *bsys, BHandle *bh);
-
-/**
- * Starts monitoring a Windows handle.
- *
- * @param bsys the object
- * @param bh handle object. Must be in active state, associated with this reactor.
- *           Must be in not monitored state.
- *           The handle object enters monitored state.
- */
-void BReactor_EnableHandle (BReactor *bsys, BHandle *bh);
-
-/**
- * Stops monitoring a Windows handle.
- *
- * @param bsys the object
- * @param bh handle object. Must be in active state, associated with this reactor.
- *           Must be in monitored state.
- *           The handle object enters not monitored state.
- */
-void BReactor_DisableHandle (BReactor *bsys, BHandle *bh);
-
-#else
+#ifndef BADVPN_USE_WINAPI
 
 /**
  * Starts monitoring a file descriptor.
@@ -541,4 +461,33 @@ void BReactorKEvent_Free (BReactorKEvent *o);
 
 #endif
 
+#ifdef BADVPN_USE_WINAPI
+
+#define BREACTOR_IOCP_EVENT_SUCCEEDED 1
+#define BREACTOR_IOCP_EVENT_FAILED 2
+#define BREACTOR_IOCP_EVENT_EXITING 3
+
+typedef void (*BReactorIOCPOverlapped_handler) (void *user, int event, DWORD bytes);
+
+typedef struct {
+    OVERLAPPED olap;
+    BReactor *reactor;
+    void *user;
+    BReactorIOCPOverlapped_handler handler;
+    LinkedList1Node iocp_list_node;
+    int is_ready;
+    LinkedList1Node ready_list_node;
+    int ready_succeeded;
+    DWORD ready_bytes;
+    DebugObject d_obj;
+} BReactorIOCPOverlapped;
+
+HANDLE BReactor_GetIOCPHandle (BReactor *reactor);
+
+void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler);
+void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o);
+void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes);
+
+#endif
+
 #endif

+ 32 - 45
system/BSignal.c

@@ -41,10 +41,9 @@ struct {
     BSignal_handler handler;
     void *user;
     #ifdef BADVPN_USE_WINAPI
-    CRITICAL_SECTION handler_mutex; // mutex to make sure only one handler is working at a time
-    HANDLE signal_sem1;
-    HANDLE signal_sem2;
-    BHandle bhandle;
+    BReactorIOCPOverlapped olap;
+    CRITICAL_SECTION iocp_handle_mutex;
+    HANDLE iocp_handle;
     #else
     BUnixSignal signal;
     #endif
@@ -54,30 +53,32 @@ struct {
 
 #ifdef BADVPN_USE_WINAPI
 
-static void signal_handle_handler (void *user)
+static void olap_handler (void *user, int event, DWORD bytes)
 {
     ASSERT(bsignal_global.initialized)
-    ASSERT(!bsignal_global.finished)
-    
-    ASSERT_FORCE(ReleaseSemaphore(bsignal_global.signal_sem2, 1, NULL))
+    ASSERT(!(event == BREACTOR_IOCP_EVENT_EXITING) || bsignal_global.finished)
     
-    BLog(BLOG_DEBUG, "Dispatching signal");
+    if (event == BREACTOR_IOCP_EVENT_EXITING) {
+        BReactorIOCPOverlapped_Free(&bsignal_global.olap);
+        return;
+    }
     
-    // call handler
-    bsignal_global.handler(bsignal_global.user);
-    return;
+    if (!bsignal_global.finished) {
+        // call handler
+        bsignal_global.handler(bsignal_global.user);
+        return;
+    }
 }
 
 static BOOL WINAPI ctrl_handler (DWORD type)
 {
-    // don't check bsignal_global.initialized to avoid a race
+    EnterCriticalSection(&bsignal_global.iocp_handle_mutex);
     
-    EnterCriticalSection(&bsignal_global.handler_mutex);
-    
-    ASSERT_FORCE(ReleaseSemaphore(bsignal_global.signal_sem1, 1, NULL))
-    ASSERT_FORCE(WaitForSingleObject(bsignal_global.signal_sem2, INFINITE) == WAIT_OBJECT_0)
+    if (bsignal_global.iocp_handle) {
+        PostQueuedCompletionStatus(bsignal_global.iocp_handle, 0, 0, &bsignal_global.olap.olap);
+    }
     
-    LeaveCriticalSection(&bsignal_global.handler_mutex);
+    LeaveCriticalSection(&bsignal_global.iocp_handle_mutex);
     
     return TRUE;
 }
@@ -112,30 +113,19 @@ int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user)
     
     #ifdef BADVPN_USE_WINAPI
     
-    InitializeCriticalSection(&bsignal_global.handler_mutex);
-    
-    if (!(bsignal_global.signal_sem1 = CreateSemaphore(NULL, 0, 1, NULL))) {
-        BLog(BLOG_ERROR, "CreateSemaphore failed");
-        goto fail1;
-    }
+    // init olap
+    BReactorIOCPOverlapped_Init(&bsignal_global.olap, bsignal_global.reactor, NULL, olap_handler);
     
-    if (!(bsignal_global.signal_sem2 = CreateSemaphore(NULL, 0, 1, NULL))) {
-        BLog(BLOG_ERROR, "CreateSemaphore failed");
-        goto fail2;
-    }
+    // init handler mutex
+    InitializeCriticalSection(&bsignal_global.iocp_handle_mutex);
     
-    // init BHandle
-    BHandle_Init(&bsignal_global.bhandle, bsignal_global.signal_sem1, signal_handle_handler, NULL);
-    if (!BReactor_AddHandle(bsignal_global.reactor, &bsignal_global.bhandle)) {
-        BLog(BLOG_ERROR, "BReactor_AddHandle failed");
-        goto fail3;
-    }
-    BReactor_EnableHandle(bsignal_global.reactor, &bsignal_global.bhandle);
+    // remember IOCP handle
+    bsignal_global.iocp_handle = BReactor_GetIOCPHandle(bsignal_global.reactor);
     
     // configure ctrl handler
     if (!SetConsoleCtrlHandler(ctrl_handler, TRUE)) {
         BLog(BLOG_ERROR, "SetConsoleCtrlHandler failed");
-        goto fail4;
+        goto fail1;
     }
     
     #else
@@ -159,14 +149,9 @@ int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user)
     return 1;
     
     #ifdef BADVPN_USE_WINAPI
-fail4:
-    BReactor_RemoveHandle(bsignal_global.reactor, &bsignal_global.bhandle);
-fail3:
-    ASSERT_FORCE(CloseHandle(bsignal_global.signal_sem2))
-fail2:
-    ASSERT_FORCE(CloseHandle(bsignal_global.signal_sem1))
 fail1:
-    DeleteCriticalSection(&bsignal_global.handler_mutex);
+    DeleteCriticalSection(&bsignal_global.iocp_handle_mutex);
+    BReactorIOCPOverlapped_Free(&bsignal_global.olap);
     #endif
     
 fail0:
@@ -180,8 +165,10 @@ void BSignal_Finish (void)
     
     #ifdef BADVPN_USE_WINAPI
     
-    // free BHandle
-    BReactor_RemoveHandle(bsignal_global.reactor, &bsignal_global.bhandle);
+    // forget IOCP handle
+    EnterCriticalSection(&bsignal_global.iocp_handle_mutex);
+    bsignal_global.iocp_handle = NULL;
+    LeaveCriticalSection(&bsignal_global.iocp_handle_mutex);
     
     #else
     

+ 0 - 1746
system/BSocket.c

@@ -1,1746 +0,0 @@
-/**
- * @file BSocket.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.
- */
-
-#ifndef BADVPN_USE_WINAPI
-#define _GNU_SOURCE
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <signal.h>
-#endif
-
-#ifdef BADVPN_LINUX
-#include <netpacket/packet.h>
-#include <net/ethernet.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-
-#include <misc/debug.h>
-#include <base/BLog.h>
-
-#include <system/BSocket.h>
-
-#include <generated/blog_channel_BSocket.h>
-
-#define HANDLER_READ 0
-#define HANDLER_WRITE 1
-#define HANDLER_ACCEPT 2
-#define HANDLER_CONNECT 3
-#define HANDLER_ERROR 4
-
-#define CONNECT_STATUS_NONE 0
-#define CONNECT_STATUS_CONNECTING 1
-#define CONNECT_STATUS_DONE 2
-#define CONNECT_STATUS_DONE_IMMEDIATELY 3
-
-int bsocket_initialized = 0;
-
-static int get_event_index (int event)
-{
-    switch (event) {
-        case BSOCKET_READ:
-            return HANDLER_READ;
-        case BSOCKET_WRITE:
-            return HANDLER_WRITE;
-        case BSOCKET_ACCEPT:
-            return HANDLER_ACCEPT;
-        case BSOCKET_CONNECT:
-            return HANDLER_CONNECT;
-        case BSOCKET_ERROR:
-            return HANDLER_ERROR;
-        default:
-            ASSERT(0)
-            return 42;
-    }
-}
-
-static int handler_events[] = {
-    [HANDLER_READ] = BSOCKET_READ,
-    [HANDLER_WRITE] = BSOCKET_WRITE,
-    [HANDLER_ACCEPT] = BSOCKET_ACCEPT,
-    [HANDLER_CONNECT] = BSOCKET_CONNECT,
-    [HANDLER_ERROR] = BSOCKET_ERROR
-};
-
-static void init_handlers (BSocket *bs)
-{
-    bs->global_handler = NULL;
-    
-    for (int i = 0; i < BSOCKET_NUM_EVENTS; i++) {
-        bs->handlers[i] = NULL;
-    }
-}
-
-static int set_nonblocking (int s)
-{
-    #ifdef BADVPN_USE_WINAPI
-    unsigned long bl = 1;
-    int res = ioctlsocket(s, FIONBIO, &bl);
-    #else
-    int res = fcntl(s, F_SETFL, O_NONBLOCK);
-    #endif
-    return res;
-}
-
-static int set_pktinfo (int s)
-{
-    #ifdef BADVPN_USE_WINAPI
-    DWORD opt = 1;
-    int res = setsockopt(s, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt));
-    #elif defined(BADVPN_FREEBSD)
-    int opt = 1;
-    int res = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)); // IP_SENDSRCADDR is the same
-    #else
-    int opt = 1;
-    int res = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
-    #endif
-    return res;
-}
-
-static int set_pktinfo6 (int s)
-{
-    #ifdef BADVPN_USE_WINAPI
-    DWORD opt = 1;
-    int res = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&opt, sizeof(opt));
-    #else
-    # ifdef IPV6_RECVPKTINFO
-    int opt = 1;
-    int res = setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt));
-    # else
-    int res = 0; // assume it succeeded
-    # endif
-    #endif
-    return res;
-}
-
-static void close_socket (int fd)
-{
-    #ifdef BADVPN_USE_WINAPI
-    int res = closesocket(fd);
-    #else
-    int res = close(fd);
-    #endif
-    ASSERT_FORCE(res == 0)
-}
-
-static int translate_error (int error)
-{
-    #ifdef BADVPN_USE_WINAPI
-    
-    switch (error) {
-        case WSAEADDRNOTAVAIL:
-            return BSOCKET_ERROR_ADDRESS_NOT_AVAILABLE;
-        case WSAEADDRINUSE:
-            return BSOCKET_ERROR_ADDRESS_IN_USE;
-        case WSAECONNRESET:
-            return BSOCKET_ERROR_CONNECTION_RESET;
-        case WSAETIMEDOUT:
-            return BSOCKET_ERROR_CONNECTION_TIMED_OUT;
-        case WSAECONNREFUSED:
-            return BSOCKET_ERROR_CONNECTION_REFUSED;
-    }
-    
-    #else
-    
-    switch (error) {
-        case EADDRNOTAVAIL:
-            return BSOCKET_ERROR_ADDRESS_NOT_AVAILABLE;
-        case EADDRINUSE:
-            return BSOCKET_ERROR_ADDRESS_IN_USE;
-        case EACCES:
-        case EPERM:
-            return BSOCKET_ERROR_ACCESS_DENIED;
-        case ECONNREFUSED:
-            return BSOCKET_ERROR_CONNECTION_REFUSED;
-        case ECONNRESET:
-            return BSOCKET_ERROR_CONNECTION_RESET;
-        case ENETUNREACH:
-            return BSOCKET_ERROR_NETWORK_UNREACHABLE;
-        case ETIMEDOUT:
-            return BSOCKET_ERROR_CONNECTION_TIMED_OUT;
-        case ENOMEM:
-            return BSOCKET_ERROR_NO_MEMORY;
-    }
-    
-    #endif
-    
-    return BSOCKET_ERROR_UNKNOWN;
-}
-
-struct sys_addr {
-    #ifdef BADVPN_USE_WINAPI
-    int len;
-    #else
-    socklen_t len;
-    #endif
-    union {
-        struct sockaddr generic;
-        struct sockaddr_in ipv4;
-        struct sockaddr_in6 ipv6;
-        #ifdef BADVPN_LINUX
-        struct sockaddr_ll packet;
-        #endif
-    } addr;
-};
-
-static void addr_socket_to_sys (struct sys_addr *out, BAddr *addr)
-{
-    switch (addr->type) {
-        case BADDR_TYPE_IPV4:
-            out->len = sizeof(out->addr.ipv4);
-            memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
-            out->addr.ipv4.sin_family = AF_INET;
-            out->addr.ipv4.sin_port = addr->ipv4.port;
-            out->addr.ipv4.sin_addr.s_addr = addr->ipv4.ip;
-            break;
-        case BADDR_TYPE_IPV6:
-            out->len = sizeof(out->addr.ipv6);
-            memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
-            out->addr.ipv6.sin6_family = AF_INET6;
-            out->addr.ipv6.sin6_port = addr->ipv6.port;
-            out->addr.ipv6.sin6_flowinfo = 0;
-            memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr->ipv6.ip, 16);
-            out->addr.ipv6.sin6_scope_id = 0;
-            break;
-        #ifdef BADVPN_LINUX
-        case BADDR_TYPE_PACKET:
-            ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET)
-            memset(&out->addr.packet, 0, sizeof(out->addr.packet));
-            out->len = sizeof(out->addr.packet);
-            out->addr.packet.sll_family = AF_PACKET;
-            out->addr.packet.sll_protocol = addr->packet.phys_proto;
-            out->addr.packet.sll_ifindex = addr->packet.interface_index;
-            out->addr.packet.sll_hatype = 1; // linux/if_arp.h: #define ARPHRD_ETHER 1
-            switch (addr->packet.packet_type) {
-                case BADDR_PACKET_PACKET_TYPE_HOST:
-                    out->addr.packet.sll_pkttype = PACKET_HOST;
-                    break;
-                case BADDR_PACKET_PACKET_TYPE_BROADCAST:
-                    out->addr.packet.sll_pkttype = PACKET_BROADCAST;
-                    break;
-                case BADDR_PACKET_PACKET_TYPE_MULTICAST:
-                    out->addr.packet.sll_pkttype = PACKET_MULTICAST;
-                    break;
-                case BADDR_PACKET_PACKET_TYPE_OTHERHOST:
-                    out->addr.packet.sll_pkttype = PACKET_OTHERHOST;
-                    break;
-                case BADDR_PACKET_PACKET_TYPE_OUTGOING:
-                    out->addr.packet.sll_pkttype = PACKET_OUTGOING;
-                    break;
-                default:
-                    ASSERT(0);
-            }
-            out->addr.packet.sll_halen = 6;
-            memcpy(out->addr.packet.sll_addr, addr->packet.phys_addr, 6);
-            break;
-        #endif
-        default:
-            ASSERT(0)
-            break;
-    }
-}
-
-static void addr_sys_to_socket (BAddr *out, struct sys_addr *addr)
-{
-    switch (addr->addr.generic.sa_family) {
-        case AF_INET:
-            ASSERT(addr->len == sizeof(struct sockaddr_in))
-            BAddr_InitIPv4(out, addr->addr.ipv4.sin_addr.s_addr, addr->addr.ipv4.sin_port);
-            break;
-        case AF_INET6:
-            ASSERT(addr->len == sizeof(struct sockaddr_in6))
-            BAddr_InitIPv6(out, addr->addr.ipv6.sin6_addr.s6_addr, addr->addr.ipv6.sin6_port);
-            break;
-        default:
-            BAddr_InitNone(out);
-            break;
-    }
-}
-
-static void dispatch_event (BSocket *bs)
-{
-    ASSERT(!bs->global_handler)
-    ASSERT(bs->current_event_index >= 0)
-    ASSERT(bs->current_event_index < BSOCKET_NUM_EVENTS)
-    ASSERT(((bs->ready_events)&~(bs->waitEvents)) == 0)
-    ASSERT(!BPending_IsSet(&bs->job))
-    
-    do {
-        // get event
-        int ev_index = bs->current_event_index;
-        int ev_mask = handler_events[ev_index];
-        int ev_dispatch = (bs->ready_events&ev_mask);
-        
-        // jump to next event, clear this event
-        bs->current_event_index++;
-        bs->ready_events &= ~ev_mask;
-        
-        ASSERT(!(bs->ready_events) || bs->current_event_index < BSOCKET_NUM_EVENTS)
-        
-        if (ev_dispatch) {
-            // if there are more events to dispatch, schedule job
-            if (bs->ready_events) {
-                BPending_Set(&bs->job);
-            }
-            
-            // dispatch this event
-            bs->handlers[ev_index](bs->handlers_user[ev_index], ev_mask);
-            return;
-        }
-    } while (bs->current_event_index < BSOCKET_NUM_EVENTS);
-    
-    ASSERT(!bs->ready_events)
-}
-
-static void job_handler (BSocket *bs)
-{
-    ASSERT(!bs->global_handler) // BSocket_RemoveGlobalEventHandler unsets the job
-    ASSERT(bs->current_event_index >= 0)
-    ASSERT(bs->current_event_index < BSOCKET_NUM_EVENTS)
-    ASSERT(((bs->ready_events)&~(bs->waitEvents)) == 0) // BSocket_DisableEvent clears events from ready_events
-    DebugObject_Access(&bs->d_obj);
-    
-    dispatch_event(bs);
-    return;
-}
-
-static void dispatch_events (BSocket *bs, int events)
-{
-    ASSERT((events&~(bs->waitEvents)) == 0)
-    ASSERT(!BPending_IsSet(&bs->job))
-    
-    // reset recv number
-    bs->recv_num = 0;
-    
-    if (bs->global_handler) {
-        if (events) {
-            bs->global_handler(bs->global_handler_user, events);
-        }
-        return;
-    }
-    
-    bs->ready_events = events;
-    bs->current_event_index = 0;
-    
-    dispatch_event(bs);
-    return;
-}
-
-static void connect_job_handler (BSocket *bs)
-{
-    ASSERT(bs->connecting_status == CONNECT_STATUS_DONE_IMMEDIATELY)
-    ASSERT((bs->waitEvents & BSOCKET_CONNECT))
-    DebugObject_Access(&bs->d_obj);
-    
-    // allow retrieving the result
-    bs->connecting_status = CONNECT_STATUS_DONE;
-    
-    if (bs->global_handler) {
-        bs->global_handler(bs->global_handler_user, BSOCKET_CONNECT);
-        return;
-    }
-    
-    bs->handlers[HANDLER_CONNECT](bs->handlers_user[HANDLER_CONNECT], BSOCKET_CONNECT);
-    return;
-}
-
-#ifdef BADVPN_USE_WINAPI
-
-static long get_wsa_events (int sock_events)
-{
-    long res = 0;
-    
-    if ((sock_events&BSOCKET_READ)) {
-        res |= FD_READ | FD_CLOSE;
-    }
-    if ((sock_events&BSOCKET_WRITE)) {
-        res |= FD_WRITE | FD_CLOSE;
-    }
-    if ((sock_events&BSOCKET_ACCEPT)) {
-        res |= FD_ACCEPT | FD_CLOSE;
-    }
-    if ((sock_events&BSOCKET_CONNECT)) {
-        res |= FD_CONNECT | FD_CLOSE;
-    }
-    
-    return res;
-}
-
-static void handle_handler (BSocket *bs)
-{
-    ASSERT(!BPending_IsSet(&bs->job))
-    DebugObject_Access(&bs->d_obj);
-    
-    // enumerate network events and reset event
-    WSANETWORKEVENTS events;
-    int res = WSAEnumNetworkEvents(bs->socket, bs->event, &events);
-    ASSERT_FORCE(res == 0)
-    
-    int returned_events = 0;
-    
-    if ((bs->waitEvents&BSOCKET_READ) && ((events.lNetworkEvents&FD_READ) || (events.lNetworkEvents&FD_CLOSE))) {
-        returned_events |= BSOCKET_READ;
-    }
-    
-    if ((bs->waitEvents&BSOCKET_WRITE) && ((events.lNetworkEvents&FD_WRITE) || (events.lNetworkEvents&FD_CLOSE))) {
-        returned_events |= BSOCKET_WRITE;
-    }
-    
-    if ((bs->waitEvents&BSOCKET_ACCEPT) && ((events.lNetworkEvents&FD_ACCEPT) || (events.lNetworkEvents&FD_CLOSE))) {
-        returned_events |= BSOCKET_ACCEPT;
-    }
-    
-    if ((bs->waitEvents&BSOCKET_CONNECT) && ((events.lNetworkEvents&FD_CONNECT) || (events.lNetworkEvents&FD_CLOSE))) {
-        returned_events |= BSOCKET_CONNECT;
-        
-        // read connection attempt result
-        ASSERT(bs->connecting_status == CONNECT_STATUS_CONNECTING)
-        bs->connecting_status = CONNECT_STATUS_DONE;
-        if (events.iErrorCode[FD_CONNECT_BIT] == 0) {
-            bs->connecting_result = BSOCKET_ERROR_NONE;
-        } else {
-            bs->connecting_result = translate_error(events.iErrorCode[FD_CONNECT_BIT]);
-        }
-    }
-    
-    if ((bs->waitEvents&BSOCKET_ERROR) && (events.lNetworkEvents&FD_CLOSE)) {
-        returned_events |= BSOCKET_ERROR;
-    }
-    
-    dispatch_events(bs, returned_events);
-    return;
-}
-
-#else
-
-static int get_reactor_fd_events (int sock_events)
-{
-    int res = 0;
-    
-    if ((sock_events&BSOCKET_READ) || (sock_events&BSOCKET_ACCEPT)) {
-        res |= BREACTOR_READ;
-    }
-    if ((sock_events&BSOCKET_WRITE) || (sock_events&BSOCKET_CONNECT)) {
-        res |= BREACTOR_WRITE;
-    }
-
-    return res;
-}
-
-static void file_descriptor_handler (BSocket *bs, int events)
-{
-    ASSERT(!BPending_IsSet(&bs->job))
-    DebugObject_Access(&bs->d_obj);
-    
-    int returned_events = 0;
-    
-    if ((bs->waitEvents&BSOCKET_READ) && ((events&BREACTOR_READ) || (events&BREACTOR_ERROR))) {
-        returned_events |= BSOCKET_READ;
-    }
-    
-    if ((bs->waitEvents&BSOCKET_WRITE) && ((events&BREACTOR_WRITE) || (events&BREACTOR_ERROR))) {
-        returned_events |= BSOCKET_WRITE;
-    }
-    
-    if ((bs->waitEvents&BSOCKET_ACCEPT) && ((events&BREACTOR_READ) || (events&BREACTOR_ERROR))) {
-        returned_events |= BSOCKET_ACCEPT;
-    }
-    
-    if ((bs->waitEvents&BSOCKET_CONNECT) && ((events&BREACTOR_WRITE) || (events&BREACTOR_ERROR))) {
-        returned_events |= BSOCKET_CONNECT;
-        
-        // read connection attempt result
-        ASSERT(bs->connecting_status == CONNECT_STATUS_CONNECTING)
-        bs->connecting_status = CONNECT_STATUS_DONE;
-        int result;
-        socklen_t result_len = sizeof(result);
-        int res = getsockopt(bs->socket, SOL_SOCKET, SO_ERROR, &result, &result_len);
-        ASSERT_FORCE(res == 0)
-        if (result == 0) {
-            bs->connecting_result = BSOCKET_ERROR_NONE;
-        } else {
-            bs->connecting_result = translate_error(result);
-        }
-    }
-    
-    if ((bs->waitEvents&BSOCKET_ERROR) && (events&BREACTOR_ERROR)) {
-        returned_events |= BSOCKET_ERROR;
-    }
-    
-    dispatch_events(bs, returned_events);
-    return;
-}
-
-#endif
-
-static int init_event_backend (BSocket *bs)
-{
-    #ifdef BADVPN_USE_WINAPI
-    if ((bs->event = WSACreateEvent()) == WSA_INVALID_EVENT) {
-        return 0;
-    }
-    BHandle_Init(&bs->bhandle, bs->event, (BHandle_handler)handle_handler, bs);
-    if (!BReactor_AddHandle(bs->bsys, &bs->bhandle)) {
-        ASSERT_FORCE(WSACloseEvent(bs->event))
-        return 0;
-    }
-    BReactor_EnableHandle(bs->bsys, &bs->bhandle);
-    #else
-    BFileDescriptor_Init(&bs->fd, bs->socket, (BFileDescriptor_handler)file_descriptor_handler, bs);
-    if (!BReactor_AddFileDescriptor(bs->bsys, &bs->fd)) {
-        return 0;
-    }
-    #endif
-    
-    return 1;
-}
-
-static void free_event_backend (BSocket *bs)
-{
-    #ifdef BADVPN_USE_WINAPI
-    BReactor_RemoveHandle(bs->bsys, &bs->bhandle);
-    ASSERT_FORCE(WSACloseEvent(bs->event))
-    #else
-    BReactor_RemoveFileDescriptor(bs->bsys, &bs->fd);
-    #endif
-}
-
-static void update_event_backend (BSocket *bs)
-{
-    #ifdef BADVPN_USE_WINAPI
-    ASSERT_FORCE(WSAEventSelect(bs->socket, bs->event, get_wsa_events(bs->waitEvents)) == 0)
-    #else
-    BReactor_SetFileDescriptorEvents(bs->bsys, &bs->fd, get_reactor_fd_events(bs->waitEvents));
-    #endif
-}
-
-static int limit_recv (BSocket *bs)
-{
-    if (bs->recv_max > 0) {
-        if (bs->recv_num >= bs->recv_max) {
-            return 1;
-        }
-        bs->recv_num++;
-    }
-    
-    return 0;
-}
-
-static int setup_pktinfo (int socket, int type, int domain)
-{
-    if (type == BSOCKET_TYPE_DGRAM) {
-        switch (domain) {
-            case BADDR_TYPE_IPV4:
-                if (set_pktinfo(socket)) {
-                    return 0;
-                }
-                break;
-            case BADDR_TYPE_IPV6:
-                if (set_pktinfo6(socket)) {
-                    return 0;
-                }
-                break;
-        }
-    }
-    
-    return 1;
-}
-
-static void setup_winsock_exts (int socket, int type, BSocket *bs)
-{
-    #ifdef BADVPN_USE_WINAPI
-    
-    if (type == BSOCKET_TYPE_DGRAM) {
-        DWORD out_bytes;
-        
-        // obtain WSARecvMsg
-        GUID guid_recv = WSAID_WSARECVMSG;
-        if (WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_recv, sizeof(guid_recv), &bs->WSARecvMsg, sizeof(bs->WSARecvMsg), &out_bytes, NULL, NULL) != 0) {
-            bs->WSARecvMsg = NULL;
-        }
-        
-        // obtain WSASendMsg
-        GUID guid_send = WSAID_WSASENDMSG;
-        if (WSAIoctl(socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_send, sizeof(guid_send), &bs->WSASendMsg, sizeof(bs->WSASendMsg), &out_bytes, NULL, NULL) != 0) {
-            bs->WSASendMsg = NULL;
-        }
-    }
-    
-    #endif
-}
-
-int BSocket_GlobalInit (void)
-{
-    ASSERT(!bsocket_initialized)
-    
-    #ifdef BADVPN_USE_WINAPI
-    
-    WORD requested = MAKEWORD(2, 2);
-    WSADATA wsadata;
-    if (WSAStartup(requested, &wsadata) != 0) {
-        goto fail0;
-    }
-    if (wsadata.wVersion != requested) {
-        goto fail1;
-    }
-    
-    #else
-    
-    struct sigaction act;
-    memset(&act, 0, sizeof(act));
-    act.sa_handler = SIG_IGN;
-    sigemptyset(&act.sa_mask);
-    act.sa_flags = 0;
-    if (sigaction(SIGPIPE, &act, NULL) < 0) {
-        BLog(BLOG_ERROR, "sigaction failed");
-        goto fail0;
-    }
-    
-    #endif
-    
-    bsocket_initialized = 1;
-    
-    return 0;
-    
-    #ifdef BADVPN_USE_WINAPI
-fail1:
-    WSACleanup();
-    #endif
-fail0:
-    return -1;
-}
-
-// NOTE: if you change something here, you might also have to change BSocket_Accept
-int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
-{
-    ASSERT(bsocket_initialized)
-    
-    // translate domain
-    int sys_domain;
-    switch (domain) {
-        case BADDR_TYPE_IPV4:
-            sys_domain = AF_INET;
-            break;
-        case BADDR_TYPE_IPV6:
-            sys_domain = AF_INET6;
-            break;
-        #ifndef BADVPN_USE_WINAPI
-        case BADDR_TYPE_UNIX:
-            sys_domain = AF_UNIX;
-            break;
-        #endif
-        #ifdef BADVPN_LINUX
-        case BADDR_TYPE_PACKET:
-            sys_domain = AF_PACKET;
-            break;
-        #endif
-        default:
-            ASSERT(0)
-            return -1;
-    }
-
-    // translate type
-    int sys_type;
-    switch (type) {
-        case BSOCKET_TYPE_STREAM:
-            sys_type = SOCK_STREAM;
-            break;
-        case BSOCKET_TYPE_DGRAM:
-            sys_type = SOCK_DGRAM;
-            break;
-        default:
-            ASSERT(0)
-            return -1;
-    }
-
-    // create socket
-    int fd = socket(sys_domain, sys_type, 0);
-    if (fd < 0) {
-        BLog(BLOG_ERROR, "socket() failed");
-        goto fail0;
-    }
-
-    // set socket nonblocking
-    if (set_nonblocking(fd) != 0) {
-        BLog(BLOG_ERROR, "set_nonblocking failed");
-        goto fail1;
-    }
-    
-    // set pktinfo if needed
-    if (!setup_pktinfo(fd, type, domain)) {
-        BLog(BLOG_ERROR, "setup_pktinfo failed");
-    }
-    
-    // setup winsock exts
-    setup_winsock_exts(fd, type, bs);
-    
-    bs->bsys = bsys;
-    bs->type = type;
-    bs->domain = domain;
-    bs->socket = fd;
-    bs->error = BSOCKET_ERROR_NONE;
-    init_handlers(bs);
-    bs->waitEvents = 0;
-    bs->connecting_status = CONNECT_STATUS_NONE;
-    bs->recv_max = BSOCKET_DEFAULT_RECV_MAX;
-    bs->recv_num = 0;
-    bs->ready_events = 0; // just initialize it so we can clear them safely from BSocket_DisableEvent
-    
-    // init job
-    BPending_Init(&bs->job, BReactor_PendingGroup(bsys), (BPending_handler)job_handler, bs);
-    
-    // init connect job
-    BPending_Init(&bs->connect_job, BReactor_PendingGroup(bsys), (BPending_handler)connect_job_handler, bs);
-    
-    // initialize event backend
-    if (!init_event_backend(bs)) {
-        BLog(BLOG_ERROR, "init_event_backend failed");
-        goto fail2;
-    }
-    
-    DebugObject_Init(&bs->d_obj);
-    
-    return 0;
-    
-fail2:
-    BPending_Free(&bs->connect_job);
-    BPending_Free(&bs->job);
-fail1:
-    close_socket(fd);
-fail0:
-    return -1;
-}
-
-#ifndef BADVPN_USE_WINAPI
-
-int BSocket_InitPipe (BSocket *bs, BReactor *bsys, int fd)
-{
-    // set socket nonblocking
-    if (set_nonblocking(fd) != 0) {
-        BLog(BLOG_ERROR, "set_nonblocking failed");
-        goto fail0;
-    }
-    
-    bs->bsys = bsys;
-    bs->type = BSOCKET_TYPE_STREAM;
-    bs->domain = BADDR_TYPE_UNIXPIPE;
-    bs->socket = fd;
-    bs->error = BSOCKET_ERROR_NONE;
-    init_handlers(bs);
-    bs->waitEvents = 0;
-    bs->connecting_status = CONNECT_STATUS_NONE;
-    bs->recv_max = BSOCKET_DEFAULT_RECV_MAX;
-    bs->recv_num = 0;
-    bs->ready_events = 0; // just initialize it so we can clear them safely from BSocket_DisableEvent
-    
-    // init job
-    BPending_Init(&bs->job, BReactor_PendingGroup(bsys), (BPending_handler)job_handler, bs);
-    
-    // init connect job
-    BPending_Init(&bs->connect_job, BReactor_PendingGroup(bsys), (BPending_handler)connect_job_handler, bs);
-    
-    // initialize event backend
-    if (!init_event_backend(bs)) {
-        BLog(BLOG_ERROR, "init_event_backend failed");
-        goto fail1;
-    }
-    
-    DebugObject_Init(&bs->d_obj);
-    
-    return 0;
-    
-fail1:
-    BPending_Free(&bs->connect_job);
-    BPending_Free(&bs->job);
-fail0:
-    return -1;
-}
-
-#endif
-
-void BSocket_Free (BSocket *bs)
-{
-    DebugObject_Free(&bs->d_obj);
-    
-    // free event backend
-    free_event_backend(bs);
-    
-    // free connect job
-    BPending_Free(&bs->connect_job);
-    
-    // free job
-    BPending_Free(&bs->job);
-    
-    // close socket
-    #ifndef BADVPN_USE_WINAPI
-    if (bs->domain != BADDR_TYPE_UNIXPIPE) {
-    #endif
-        close_socket(bs->socket);
-    #ifndef BADVPN_USE_WINAPI
-    }
-    #endif
-}
-
-void BSocket_SetRecvMax (BSocket *bs, int max)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    ASSERT(max > 0 || max == -1)
-    
-    bs->recv_max = max;
-    bs->recv_num = 0;
-}
-
-int BSocket_GetError (BSocket *bs)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    return bs->error;
-}
-
-void BSocket_AddGlobalEventHandler (BSocket *bs, BSocket_handler handler, void *user)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    ASSERT(handler)
-    ASSERT(!bs->global_handler)
-    for (int i = 0; i < BSOCKET_NUM_EVENTS; i++) {
-        ASSERT(!bs->handlers[i])
-    }
-    
-    bs->global_handler = handler;
-    bs->global_handler_user = user;
-    
-    // stop event dispatching job
-    BPending_Unset(&bs->job);
-}
-
-void BSocket_RemoveGlobalEventHandler (BSocket *bs)
-{
-    ASSERT(bs->global_handler)
-    DebugObject_Access(&bs->d_obj);
-    
-    bs->global_handler = NULL;
-    bs->waitEvents = 0;
-}
-
-void BSocket_SetGlobalEvents (BSocket *bs, int events)
-{
-    ASSERT(bs->global_handler)
-    DebugObject_Access(&bs->d_obj);
-    
-    // update events
-    bs->waitEvents = events;
-    
-    // give new events to event backend
-    update_event_backend(bs);
-}
-
-void BSocket_AddEventHandler (BSocket *bs, uint8_t event, BSocket_handler handler, void *user)
-{
-    ASSERT(handler)
-    ASSERT(!bs->global_handler)
-    DebugObject_Access(&bs->d_obj);
-    
-    // get index
-    int i = get_event_index(event);
-    
-    // event must not have handler
-    ASSERT(!bs->handlers[i])
-    
-    // change handler
-    bs->handlers[i] = handler;
-    bs->handlers_user[i] = user;
-}
-
-void BSocket_RemoveEventHandler (BSocket *bs, uint8_t event)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    // get table index
-    int i = get_event_index(event);
-    
-    // event must have handler
-    ASSERT(bs->handlers[i])
-    
-    // disable event if enabled
-    if (bs->waitEvents&event) {
-        BSocket_DisableEvent(bs, event);
-    }
-    
-    // change handler
-    bs->handlers[i] = NULL;
-}
-
-void BSocket_EnableEvent (BSocket *bs, uint8_t event)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    #ifndef NDEBUG
-    // check event and incompatible events
-    switch (event) {
-        case BSOCKET_READ:
-        case BSOCKET_WRITE:
-            ASSERT(!(bs->waitEvents&BSOCKET_ACCEPT))
-            ASSERT(!(bs->waitEvents&BSOCKET_CONNECT))
-            break;
-        case BSOCKET_ACCEPT:
-            ASSERT(!(bs->waitEvents&BSOCKET_READ))
-            ASSERT(!(bs->waitEvents&BSOCKET_WRITE))
-            ASSERT(!(bs->waitEvents&BSOCKET_CONNECT))
-            break;
-        case BSOCKET_CONNECT:
-            ASSERT(!(bs->waitEvents&BSOCKET_READ))
-            ASSERT(!(bs->waitEvents&BSOCKET_WRITE))
-            ASSERT(!(bs->waitEvents&BSOCKET_ACCEPT))
-            break;
-        case BSOCKET_ERROR:
-            break;
-        default:
-            ASSERT(0)
-            break;
-    }
-    #endif
-    
-    // event must have handler
-    ASSERT(bs->handlers[get_event_index(event)])
-    
-    // event must not be enabled
-    ASSERT(!(bs->waitEvents&event))
-    
-    // update events
-    bs->waitEvents |= event;
-    
-    // give new events to event backend
-    update_event_backend(bs);
-    
-    // set connect job
-    if (bs->connecting_status == CONNECT_STATUS_DONE_IMMEDIATELY && event == BSOCKET_CONNECT) {
-        BPending_Set(&bs->connect_job);
-    }
-}
-
-void BSocket_DisableEvent (BSocket *bs, uint8_t event)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    // check event and get index
-    int index = get_event_index(event);
-    
-    // event must have handler
-    ASSERT(bs->handlers[index])
-    
-    // event must be enabled
-    ASSERT(bs->waitEvents&event)
-    
-    // update events
-    bs->waitEvents &= ~event;
-    bs->ready_events &= ~event;
-    
-    // give new events to event backend
-    update_event_backend(bs);
-    
-    // unset connect job
-    if (!(bs->connecting_status == CONNECT_STATUS_DONE_IMMEDIATELY && (bs->waitEvents & BSOCKET_CONNECT))) {
-        BPending_Unset(&bs->connect_job);
-    }
-}
-
-int BSocket_Connect (BSocket *bs, BAddr *addr, int later)
-{
-    ASSERT(addr)
-    ASSERT(!BAddr_IsInvalid(addr))
-    ASSERT(later == 0 || later == 1)
-    ASSERT(bs->connecting_status == CONNECT_STATUS_NONE)
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sys_addr sysaddr;
-    addr_socket_to_sys(&sysaddr, addr);
-
-    if (connect(bs->socket, &sysaddr.addr.generic, sysaddr.len) < 0) {
-        int error;
-        #ifdef BADVPN_USE_WINAPI
-        switch ((error = WSAGetLastError())) {
-            case WSAEWOULDBLOCK:
-                bs->connecting_status = CONNECT_STATUS_CONNECTING;
-                bs->error = BSOCKET_ERROR_IN_PROGRESS;
-                return -1;
-        }
-        #else
-        switch ((error = errno)) {
-            case EINPROGRESS:
-                bs->connecting_status = CONNECT_STATUS_CONNECTING;
-                bs->error = BSOCKET_ERROR_IN_PROGRESS;
-                return -1;
-        }
-        #endif
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    if (later) {
-        // set connect result
-        bs->connecting_status = CONNECT_STATUS_DONE_IMMEDIATELY;
-        bs->connecting_result = BSOCKET_ERROR_NONE;
-        
-        // set connect job
-        if ((bs->waitEvents & BSOCKET_CONNECT)) {
-            BPending_Set(&bs->connect_job);
-        }
-        
-        // return in progress error
-        bs->error = BSOCKET_ERROR_IN_PROGRESS;
-        return -1;
-    }
-
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}
-
-int BSocket_GetConnectResult (BSocket *bs)
-{
-    ASSERT(bs->connecting_status == CONNECT_STATUS_DONE)
-    DebugObject_Access(&bs->d_obj);
-
-    bs->connecting_status = CONNECT_STATUS_NONE;
-    
-    return bs->connecting_result;
-}
-
-int BSocket_Bind (BSocket *bs, BAddr *addr)
-{
-    ASSERT(addr)
-    ASSERT(!BAddr_IsInvalid(addr))
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sys_addr sysaddr;
-    addr_socket_to_sys(&sysaddr, addr);
-    
-    if (bs->type == BSOCKET_TYPE_STREAM) {
-        #ifdef BADVPN_USE_WINAPI
-        BOOL optval = TRUE;
-        #else
-        int optval = 1;
-        #endif
-        if (setsockopt(bs->socket, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(optval)) < 0) {
-            BLog(BLOG_ERROR, "setsockopt failed");
-        }
-    }
-    
-    if (bind(bs->socket, &sysaddr.addr.generic, sysaddr.len) < 0) {
-        #ifdef BADVPN_USE_WINAPI
-        int error = WSAGetLastError();
-        #else
-        int error = errno;
-        #endif
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}
-
-int BSocket_Listen (BSocket *bs, int backlog)
-{
-    ASSERT(bs->type == BSOCKET_TYPE_STREAM)
-    DebugObject_Access(&bs->d_obj);
-    
-    if (backlog < 0) {
-        backlog = BSOCKET_DEFAULT_BACKLOG;
-    }
-    
-    if (listen(bs->socket, backlog) < 0) {
-        #ifdef BADVPN_USE_WINAPI
-        int error = WSAGetLastError();
-        #else
-        int error = errno;
-        #endif
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}
-
-int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr)
-{
-    ASSERT(bs->type == BSOCKET_TYPE_STREAM)
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sys_addr sysaddr;
-    sysaddr.len = sizeof(sysaddr.addr);
-    
-    int fd = accept(bs->socket, &sysaddr.addr.generic, &sysaddr.len);
-    if (fd < 0) {
-        int error;
-        #ifdef BADVPN_USE_WINAPI
-        switch ((error = WSAGetLastError())) {
-            case WSAEWOULDBLOCK:
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        #else
-        switch ((error = errno)) {
-            case EAGAIN:
-            #if EAGAIN != EWOULDBLOCK
-            case EWOULDBLOCK:
-            #endif
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        #endif
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    if (!newsock) {
-        close_socket(fd);
-    } else {
-        // set nonblocking
-        if (set_nonblocking(fd) != 0) {
-            BLog(BLOG_ERROR, "set_nonblocking failed");
-            goto fail0;
-        }
-        
-        // set pktinfo if needed
-        if (!setup_pktinfo(fd, bs->type, bs->domain)) {
-            BLog(BLOG_ERROR, "setup_pktinfo failed");
-        }
-        
-        // setup winsock exts
-        setup_winsock_exts(fd, bs->type, newsock);
-        
-        newsock->bsys = bs->bsys;
-        newsock->type = bs->type;
-        newsock->domain = bs->domain;
-        newsock->socket = fd;
-        newsock->error = BSOCKET_ERROR_NONE;
-        init_handlers(newsock);
-        newsock->waitEvents = 0;
-        newsock->connecting_status = 0;
-        newsock->recv_max = BSOCKET_DEFAULT_RECV_MAX;
-        newsock->recv_num = 0;
-        newsock->ready_events = 0; // just initialize it so we can clear them safely from BSocket_DisableEvent
-        
-        // init job
-        BPending_Init(&newsock->job, BReactor_PendingGroup(bs->bsys), (BPending_handler)job_handler, newsock);
-        
-        // init connect job
-        BPending_Init(&newsock->connect_job, BReactor_PendingGroup(bs->bsys), (BPending_handler)connect_job_handler, newsock);
-    
-        if (!init_event_backend(newsock)) {
-            BLog(BLOG_ERROR, "init_event_backend failed");
-            goto fail1;
-        }
-        
-        // init debug object
-        DebugObject_Init(&newsock->d_obj);
-    }
-    
-    // return client address
-    if (addr) {
-        addr_sys_to_socket(addr, &sysaddr);
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-    
-fail1:
-    BPending_Free(&newsock->connect_job);
-    BPending_Free(&newsock->job);
-fail0:
-    close_socket(fd);
-    bs->error = BSOCKET_ERROR_UNKNOWN;
-    return -1;
-}
-
-int BSocket_Send (BSocket *bs, uint8_t *data, int len)
-{
-    ASSERT(len >= 0)
-    ASSERT(bs->type == BSOCKET_TYPE_STREAM)
-    DebugObject_Access(&bs->d_obj);
-    
-    int bytes;
-    #ifndef BADVPN_USE_WINAPI
-    if (bs->domain == BADDR_TYPE_UNIXPIPE) {
-        bytes = write(bs->socket, data, len);
-    } else {
-    #endif
-        bytes = send(bs->socket, data, len, 0);
-    #ifndef BADVPN_USE_WINAPI
-    }
-    #endif
-    if (bytes < 0) {
-        int error;
-        #ifdef BADVPN_USE_WINAPI
-        switch ((error = WSAGetLastError())) {
-            case WSAEWOULDBLOCK:
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        #else
-        switch ((error = errno)) {
-            case EAGAIN:
-            #if EAGAIN != EWOULDBLOCK
-            case EWOULDBLOCK:
-            #endif
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        #endif
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return bytes;
-}
-
-int BSocket_Recv (BSocket *bs, uint8_t *data, int len)
-{
-    ASSERT(len >= 0)
-    ASSERT(bs->type == BSOCKET_TYPE_STREAM)
-    DebugObject_Access(&bs->d_obj);
-    
-    if (limit_recv(bs)) {
-        bs->error = BSOCKET_ERROR_LATER;
-        return -1;
-    }
-    
-    int bytes;
-    #ifndef BADVPN_USE_WINAPI
-    if (bs->domain == BADDR_TYPE_UNIXPIPE) {
-        bytes = read(bs->socket, data, len);
-    } else {
-    #endif
-        bytes = recv(bs->socket, data, len, 0);
-    #ifndef BADVPN_USE_WINAPI
-    }
-    #endif
-    if (bytes < 0) {
-        int error;
-        #ifdef BADVPN_USE_WINAPI
-        switch ((error = WSAGetLastError())) {
-            case WSAEWOULDBLOCK:
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        #else
-        switch ((error = errno)) {
-            case EAGAIN:
-            #if EAGAIN != EWOULDBLOCK
-            case EWOULDBLOCK:
-            #endif
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        #endif
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return bytes;
-}
-
-int BSocket_SendToFrom (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAddr *local_addr)
-{
-    ASSERT(len >= 0)
-    ASSERT(addr)
-    ASSERT(!BAddr_IsInvalid(addr))
-    ASSERT(local_addr)
-    ASSERT(bs->type == BSOCKET_TYPE_DGRAM)
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sys_addr remote_sysaddr;
-    addr_socket_to_sys(&remote_sysaddr, addr);
-    
-    #ifdef BADVPN_USE_WINAPI
-    
-    WSABUF buf;
-    buf.len = len;
-    buf.buf = data;
-    
-    DWORD bytes;
-    
-    if (!bs->WSASendMsg) {
-        if (WSASendTo(bs->socket, &buf, 1, &bytes, 0, &remote_sysaddr.addr.generic, remote_sysaddr.len, NULL, NULL) != 0) {
-            int error;
-            switch ((error = WSAGetLastError())) {
-                case WSAEWOULDBLOCK:
-                    bs->error = BSOCKET_ERROR_LATER;
-                    return -1;
-            }
-            
-            bs->error = translate_error(error);
-            return -1;
-        }
-    } else {
-        union {
-            char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
-            char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))];
-        } cdata;
-        
-        WSAMSG msg;
-        memset(&msg, 0, sizeof(msg));
-        msg.name = &remote_sysaddr.addr.generic;
-        msg.namelen = remote_sysaddr.len;
-        msg.lpBuffers = &buf;
-        msg.dwBufferCount = 1;
-        msg.Control.buf = (char *)&cdata;
-        msg.Control.len = sizeof(cdata);
-        
-        int sum = 0;
-        
-        WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&msg);
-        
-        switch (local_addr->type) {
-            case BADDR_TYPE_NONE:
-                break;
-            case BADDR_TYPE_IPV4: {
-                memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
-                cmsg->cmsg_level = IPPROTO_IP;
-                cmsg->cmsg_type = IP_PKTINFO;
-                cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
-                struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg);
-                pktinfo->ipi_addr.s_addr = local_addr->ipv4;
-                sum += WSA_CMSG_SPACE(sizeof(struct in_pktinfo));
-            } break;
-            case BADDR_TYPE_IPV6: {
-                memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)));
-                cmsg->cmsg_level = IPPROTO_IPV6;
-                cmsg->cmsg_type = IPV6_PKTINFO;
-                cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in6_pktinfo));
-                struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg);
-                memcpy(pktinfo->ipi6_addr.s6_addr, local_addr->ipv6, 16);
-                sum += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo));
-            } break;
-            default:
-                ASSERT(0);
-        }
-        
-        msg.Control.len = sum;
-        
-        // WSASendMsg chokes on empty control block
-        if (msg.Control.len == 0) {
-            msg.Control.buf = NULL;
-        }
-        
-        if (bs->WSASendMsg(bs->socket, &msg, 0, &bytes, NULL, NULL) != 0) {
-            int error;
-            switch ((error = WSAGetLastError())) {
-                case WSAEWOULDBLOCK:
-                    bs->error = BSOCKET_ERROR_LATER;
-                    return -1;
-            }
-            
-            bs->error = translate_error(error);
-            return -1;
-        }
-    }
-    
-    #else
-    
-    struct iovec iov;
-    iov.iov_base = data;
-    iov.iov_len = len;
-    
-    union {
-        #ifdef BADVPN_FREEBSD
-        char in[CMSG_SPACE(sizeof(struct in_addr))];
-        #else
-        char in[CMSG_SPACE(sizeof(struct in_pktinfo))];
-        #endif
-        char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-    } cdata;
-    
-    struct msghdr msg;
-    memset(&msg, 0, sizeof(msg));
-    msg.msg_name = &remote_sysaddr.addr.generic;
-    msg.msg_namelen = remote_sysaddr.len;
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_control = &cdata;
-    msg.msg_controllen = sizeof(cdata);
-    
-    int sum = 0;
-    
-    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
-    
-    switch (local_addr->type) {
-        case BADDR_TYPE_NONE:
-            break;
-        case BADDR_TYPE_IPV4: {
-            #ifdef BADVPN_FREEBSD
-            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr)));
-            cmsg->cmsg_level = IPPROTO_IP;
-            cmsg->cmsg_type = IP_SENDSRCADDR;
-            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
-            struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg);
-            addrinfo->s_addr = local_addr->ipv4;;
-            sum += CMSG_SPACE(sizeof(struct in_addr));
-            #else
-            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
-            cmsg->cmsg_level = IPPROTO_IP;
-            cmsg->cmsg_type = IP_PKTINFO;
-            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
-            struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
-            pktinfo->ipi_spec_dst.s_addr = local_addr->ipv4;
-            sum += CMSG_SPACE(sizeof(struct in_pktinfo));
-            #endif
-        } break;
-        case BADDR_TYPE_IPV6: {
-            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
-            cmsg->cmsg_level = IPPROTO_IPV6;
-            cmsg->cmsg_type = IPV6_PKTINFO;
-            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
-            struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
-            memcpy(pktinfo->ipi6_addr.s6_addr, local_addr->ipv6, 16);
-            sum += CMSG_SPACE(sizeof(struct in6_pktinfo));
-        } break;
-        default:
-            ASSERT(0);
-    }
-    
-    msg.msg_controllen = sum;
-    
-    // FreeBSD chokes on empty control block
-    if (msg.msg_controllen == 0) {
-        msg.msg_control = NULL;
-    }
-    
-    int bytes = sendmsg(bs->socket, &msg, 0);
-    if (bytes < 0) {
-        int error;
-        switch ((error = errno)) {
-            case EAGAIN:
-            #if EAGAIN != EWOULDBLOCK
-            case EWOULDBLOCK:
-            #endif
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    #endif
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return bytes;
-}
-
-int BSocket_RecvFromTo (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAddr *local_addr)
-{
-    ASSERT(len >= 0)
-    ASSERT(addr)
-    ASSERT(local_addr)
-    ASSERT(bs->type == BSOCKET_TYPE_DGRAM)
-    DebugObject_Access(&bs->d_obj);
-    
-    if (limit_recv(bs)) {
-        bs->error = BSOCKET_ERROR_LATER;
-        return -1;
-    }
-    
-    struct sys_addr remote_sysaddr;
-    remote_sysaddr.len = sizeof(remote_sysaddr.addr);
-    
-    #ifdef BADVPN_USE_WINAPI
-    
-    WSABUF buf;
-    buf.len = len;
-    buf.buf = data;
-    
-    DWORD bytes;
-    
-    WSAMSG msg;
-    
-    if (!bs->WSARecvMsg) {
-        DWORD flags = 0;
-        INT fromlen = remote_sysaddr.len;
-        if (WSARecvFrom(bs->socket, &buf, 1, &bytes, &flags, &remote_sysaddr.addr.generic, &fromlen, NULL, NULL) != 0) {
-            int error;
-            switch ((error = WSAGetLastError())) {
-                case WSAEWOULDBLOCK:
-                    bs->error = BSOCKET_ERROR_LATER;
-                    return -1;
-            }
-            
-            bs->error = translate_error(error);
-            return -1;
-        }
-        
-        remote_sysaddr.len = fromlen;
-    } else {
-        union {
-            char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))];
-            char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))];
-        } cdata;
-        
-        memset(&msg, 0, sizeof(msg));
-        msg.name = &remote_sysaddr.addr.generic;
-        msg.namelen = remote_sysaddr.len;
-        msg.lpBuffers = &buf;
-        msg.dwBufferCount = 1;
-        msg.Control.buf = (char *)&cdata;
-        msg.Control.len = sizeof(cdata);
-        
-        if (bs->WSARecvMsg(bs->socket, &msg, &bytes, NULL, NULL) != 0) {
-            int error;
-            switch ((error = WSAGetLastError())) {
-                case WSAEWOULDBLOCK:
-                    bs->error = BSOCKET_ERROR_LATER;
-                    return -1;
-            }
-            
-            bs->error = translate_error(error);
-            return -1;
-        }
-        
-        remote_sysaddr.len = msg.namelen;
-    }
-    
-    #else
-        
-    struct iovec iov;
-    iov.iov_base = data;
-    iov.iov_len = len;
-    
-    union {
-        #ifdef BADVPN_FREEBSD
-        char in[CMSG_SPACE(sizeof(struct in_addr))];
-        #else
-        char in[CMSG_SPACE(sizeof(struct in_pktinfo))];
-        #endif
-        char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-    } cdata;
-    
-    struct msghdr msg;
-    memset(&msg, 0, sizeof(msg));
-    msg.msg_name = &remote_sysaddr.addr.generic;
-    msg.msg_namelen = remote_sysaddr.len;
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_control = &cdata;
-    msg.msg_controllen = sizeof(cdata);
-    
-    int bytes = recvmsg(bs->socket, &msg, 0);
-    if (bytes < 0) {
-        int error;
-        switch ((error = errno)) {
-            case EAGAIN:
-            #if EAGAIN != EWOULDBLOCK
-            case EWOULDBLOCK:
-            #endif
-                bs->error = BSOCKET_ERROR_LATER;
-                return -1;
-        }
-        
-        bs->error = translate_error(error);
-        return -1;
-    }
-    
-    remote_sysaddr.len = msg.msg_namelen;
-    
-    #endif
-    
-    addr_sys_to_socket(addr, &remote_sysaddr);
-    BIPAddr_InitInvalid(local_addr);
-    
-    #ifdef BADVPN_USE_WINAPI
-    
-    if (bs->WSARecvMsg) {
-        WSACMSGHDR *cmsg;
-        for (cmsg = WSA_CMSG_FIRSTHDR(&msg); cmsg; cmsg = WSA_CMSG_NXTHDR(&msg, cmsg)) {
-            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
-                struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg);
-                BIPAddr_InitIPv4(local_addr, pktinfo->ipi_addr.s_addr);
-            }
-            else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
-                struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg);
-                BIPAddr_InitIPv6(local_addr, pktinfo->ipi6_addr.s6_addr);
-            }
-        }
-    }
-    
-    #else
-    
-    struct cmsghdr *cmsg;
-    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-        #ifdef BADVPN_FREEBSD
-        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
-            struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg);
-            BIPAddr_InitIPv4(local_addr, addrinfo->s_addr);
-        }
-        #else
-        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
-            struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
-            BIPAddr_InitIPv4(local_addr, pktinfo->ipi_addr.s_addr);
-        }
-        #endif
-        else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
-            struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
-            BIPAddr_InitIPv6(local_addr, pktinfo->ipi6_addr.s6_addr);
-        }
-    }
-    
-    #endif
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return bytes;
-}
-
-int BSocket_GetPeerName (BSocket *bs, BAddr *addr)
-{
-    ASSERT(addr)
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sys_addr sysaddr;
-    sysaddr.len = sizeof(sysaddr.addr);
-    
-    if (getpeername(bs->socket, &sysaddr.addr.generic, &sysaddr.len) < 0) {
-        bs->error = BSOCKET_ERROR_UNKNOWN;
-        return -1;
-    }
-    
-    addr_sys_to_socket(addr, &sysaddr);
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}
-
-#ifndef BADVPN_USE_WINAPI
-
-static int create_unix_sysaddr (struct sockaddr_un *addr, size_t *addr_len, const char *path)
-{
-    size_t path_len = strlen(path);
-    
-    if (path_len == 0) {
-        BLog(BLOG_ERROR, "path empty");
-        return 0;
-    }
-    
-    addr->sun_family = AF_UNIX;
-    
-    if (path_len >= sizeof(addr->sun_path)) {
-        BLog(BLOG_ERROR, "path too long");
-        return 0;
-    }
-    
-    strcpy(addr->sun_path, path);
-    
-    *addr_len = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
-    
-    return 1;
-}
-
-int BSocket_BindUnix (BSocket *bs, const char *path)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sockaddr_un sys_addr;
-    size_t addr_len;
-    
-    if (!create_unix_sysaddr(&sys_addr, &addr_len, path)) {
-        bs->error = BSOCKET_ERROR_UNKNOWN;
-        return -1;
-    }
-    
-    if (bind(bs->socket, (struct sockaddr *)&sys_addr, addr_len) < 0) {
-        bs->error = translate_error(errno);
-        return -1;
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}
-
-int BSocket_ConnectUnix (BSocket *bs, const char *path)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    struct sockaddr_un sys_addr;
-    size_t addr_len;
-    
-    if (!create_unix_sysaddr(&sys_addr, &addr_len, path)) {
-        bs->error = BSOCKET_ERROR_UNKNOWN;
-        return -1;
-    }
-    
-    if (connect(bs->socket, (struct sockaddr *)&sys_addr, addr_len) < 0) {
-        bs->error = translate_error(errno);
-        return -1;
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}
-
-#endif
-
-BReactor * BSocket_Reactor (BSocket *bs)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    return bs->bsys;
-}
-
-int BSocket_SockFd (BSocket *bs)
-{
-    DebugObject_Access(&bs->d_obj);
-    
-    return bs->socket;
-}
-
-int BSocket_SetSendBuffer (BSocket *bs, int buf_size)
-{
-    ASSERT(buf_size > 0)
-    DebugObject_Access(&bs->d_obj);
-    
-    if (setsockopt(bs->socket, SOL_SOCKET, SO_SNDBUF, (void *)&buf_size, sizeof(buf_size)) < 0) {
-        bs->error = translate_error(errno);
-        return -1;
-    }
-    
-    bs->error = BSOCKET_ERROR_NONE;
-    return 0;
-}

+ 0 - 492
system/BSocket.h

@@ -1,492 +0,0 @@
-/**
- * @file BSocket.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * A wrapper around OS-specific socket functions, integrated into
- * the event system.
- */
-
-#ifndef BADVPN_SYSTEM_BSOCKET_H
-#define BADVPN_SYSTEM_BSOCKET_H
-
-#ifdef BADVPN_USE_WINAPI
-    #include <winsock2.h>
-    #ifndef BADVPN_USE_SHIPPED_MSWSOCK
-        #include <mswsock.h>
-    #else
-        #include <misc/mswsock.h>
-    #endif
-#endif
-
-#include <misc/debug.h>
-#include <system/BAddr.h>
-#include <system/BReactor.h>
-#include <base/DebugObject.h>
-#include <base/BPending.h>
-
-// errors
-#define BSOCKET_ERROR_NONE 0
-#define BSOCKET_ERROR_UNKNOWN 1
-#define BSOCKET_ERROR_LATER 2
-#define BSOCKET_ERROR_IN_PROGRESS 3
-#define BSOCKET_ERROR_ACCESS_DENIED 4
-#define BSOCKET_ERROR_ADDRESS_NOT_AVAILABLE 5
-#define BSOCKET_ERROR_ADDRESS_IN_USE 6
-#define BSOCKET_ERROR_CONNECTION_REFUSED 7
-#define BSOCKET_ERROR_CONNECTION_TIMED_OUT 8
-#define BSOCKET_ERROR_CONNECTION_RESET 9
-#define BSOCKET_ERROR_NETWORK_UNREACHABLE 10
-#define BSOCKET_ERROR_NO_MEMORY 11
-
-// socket types
-#define BSOCKET_TYPE_STREAM 1
-#define BSOCKET_TYPE_DGRAM 2
-
-// socket events
-#define BSOCKET_READ (1 << 0)
-#define BSOCKET_WRITE (1 << 1)
-#define BSOCKET_ACCEPT (1 << 2)
-#define BSOCKET_CONNECT (1 << 3)
-#define BSOCKET_ERROR (1 << 4)
-#define BSOCKET_NUM_EVENTS 5
-
-// default backlog if backlog is <0
-#define BSOCKET_DEFAULT_BACKLOG 128
-
-// default limit for number of consecutive receive operations
-// must be -1 (no limit) or >0
-#define BSOCKET_DEFAULT_RECV_MAX 2
-
-struct BSocket_t;
-
-/**
- * Handler function called when an event is detected on the socket.
- * 
- * If the socket does not have a global event handler registered:
- * The event parameter is the event that was detected.
- * It is guaranteed that that the corresponding event handler is registered
- * and the event is enabled.
- * 
- * If the socket has a global event handler registered:
- * The event parameter is a bitmask of detected events. It is guaranteed
- * that it is a subset of enabled events, and contains at least one event.
- * 
- * It is guaranteed that the handler returns control to the reactor immediately.
- * 
- * @param user as in {@link BSocket_AddEventHandler} or {@link BSocket_AddGlobalEventHandler}
- * @param event events that were detected, see above
- */
-typedef void (*BSocket_handler) (void *user, int event);
-
-/**
- * A wrapper around OS-specific socket functions, integrated into
- * the event system.
- *
- * To simplify implementation, most functions just call the corresponding
- * socket function. Only required and most common errors are translated.
- */
-typedef struct BSocket_t {
-    DebugObject d_obj;
-    BReactor *bsys;
-    int type;
-    int domain;
-    int socket;
-    int error;
-    BSocket_handler global_handler;
-    void *global_handler_user;
-    BSocket_handler handlers[BSOCKET_NUM_EVENTS];
-    void *handlers_user[BSOCKET_NUM_EVENTS];
-    uint8_t waitEvents;
-    int connecting_status; // 0 not connecting, 1 connecting, 2 finished
-    int connecting_result;
-    int recv_max;
-    int recv_num;
-    
-    // event dispatching
-    int ready_events;
-    int current_event_index;
-    BPending job;
-    BPending connect_job;
-    
-    #ifdef BADVPN_USE_WINAPI
-    WSAEVENT event;
-    BHandle bhandle;
-    LPFN_WSASENDMSG WSASendMsg;
-    LPFN_WSARECVMSG WSARecvMsg;
-    #else
-    BFileDescriptor fd;
-    #endif
-} BSocket;
-
-/**
- * Initializes global socket data.
- * This must be called once in program before sockets are used.
- * Must not be called again after success.
- * 
- * On unix-like systems, this sets the signal disposition for SIGPIPE to SIG_IGN.
- *
- * @return 0 for success, -1 for failure
- */
-int BSocket_GlobalInit (void) WARN_UNUSED;
-
-/**
- * Initializes a socket.
- * {@link BSocket_GlobalInit} must have been done.
- *
- * @param bs the object
- * @param bsys {@link BReactor} to operate in
- * @param domain domain (same as address type). Must be one of BADDR_TYPE_IPV4, BADDR_TYPE_IPV6
- *               and BADDR_TYPE_UNIX (non-Windows only).
- * @param type socket type. Must be one of BSOCKET_TYPE_STREAM and BSOCKET_TYPE_DGRAM.
- * @return 0 for success,
- *         -1 for failure
- */
-int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type) WARN_UNUSED;
-
-#ifndef BADVPN_USE_WINAPI
-
-/**
- * Initializes a socket object backed by a pipe.
- * The resulting socket has type BSOCKET_TYPE_STREAM.
- * 
- * @param bs the object
- * @param bsys {@link BReactor} to operate in
- * @param fd pipe file descriptor. Will be set to non-blocking mode. Will NOT be closed when
- *           the socket is freed.
- * @return 0 for success, -1 for failure
- */
-int BSocket_InitPipe (BSocket *bs, BReactor *bsys, int fd);
-
-#endif
-
-/**
- * Frees a socket.
- *
- * @param bs the object
- */
-void BSocket_Free (BSocket *bs);
-
-/**
- * Sets the maximum number of consecutive receive operations.
- * This limit prevents starvation that might occur when data is being
- * received on a socket faster than in can be processed.
- * The default limit is BSOCKET_DEFAULT_RECV_MAX.
- *
- * @param bs the object
- * @param max number of consecutive receive operations allowed. Muse be >0,
- *            or -1 for no limit.
- */
-void BSocket_SetRecvMax (BSocket *bs, int max);
-
-/**
- * Returns the socket's current error code.
- *
- * @param bs the object
- */
-int BSocket_GetError (BSocket *bs);
-
-/**
- * Registers a socket-global event handler.
- * The socket-global event handler must not be registered.
- * No event-specific handlers must be registered.
- * When the handler is invoked, it is passed a bitmask of events
- * that occured, instead of a single event.
- *
- * @param bs the object
- * @param handler event handler
- * @param user value to be passed to event handler
- */
-void BSocket_AddGlobalEventHandler (BSocket *bs, BSocket_handler handler, void *user);
-
-/**
- * Unregisters the socket-global event handler.
- * The socket-global event handler must be registered.
- *
- * @param bs the object
- * @param handler event handler
- * @param user value to be passed to event handler
- */
-void BSocket_RemoveGlobalEventHandler (BSocket *bs);
-
-/**
- * Sets events for the socket-global event handler.
- * The socket-global event handler must be registered.
- *
- * @param bs the object
- * @param events bitmask containing socket events the user is interested in
- */
-void BSocket_SetGlobalEvents (BSocket *bs, int events);
-
-/**
- * Registers an event handler for a socket event.
- * When the handler is registered, the corresponding event will
- * initially be disabled.
- * The event must be valid and must not have a handler.
- * The socket-global event handler must not be registered.
- *
- * @param bs the object
- * @param event event to register handler for
- * @param handler event handler
- * @param user value to be passed to event handler
- */
-void BSocket_AddEventHandler (BSocket *bs, uint8_t event, BSocket_handler handler, void *user);
-
-/**
- * Unregisters an event handler for a socket event.
- * The event must be valid and must have a handler.
- *
- * @param bs the object
- * @param event event to unregister handler for
- */
-void BSocket_RemoveEventHandler (BSocket *bs, uint8_t event);
-
-/**
- * Enables a socket event.
- * The event must be valid, must not be enabled, and must have a handler.
- *
- * @param bs the object
- * @param event event to enable
- */
-void BSocket_EnableEvent (BSocket *bs, uint8_t event);
-
-/**
- * Disables a socket event.
- * The event must be valid, must be enabled, and must have a handler.
- *
- * @param bs the object
- * @param event event to enable
- */
-void BSocket_DisableEvent (BSocket *bs, uint8_t event);
-
-/**
- * Connects the socket to the specifed address, or starts a connection attempt.
- *
- * There must be no pending connection attempt.
- * 
- * For stream sockets, the user will have to wait for the connection result. See the
- * BSOCKET_ERROR_IN_PROGRESS error for details.
- *
- * Datagram sockets can be connected at any time, since connecting such a socket only means
- * specifying an addres where datagrams will be sent and received from.
- * An associated address can be removed by specifying a BADDR_TYPE_NONE address.
- *
- * @param bs the object
- * @param addr remote address. Must not be an invalid address.
- * @param later if true, even if the connection succeeded right away, fail with BSOCKET_ERROR_IN_PROGRESS,
- *              and report success in the handler. Must be 0 or 1.
- * @return 0 for immediate success,
- *         -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_IN_PROGRESS the socket is a stream socket and the connection attempt has started.
- *                                         The user should wait for the BSOCKET_CONNECT event and obtain the
- *                                         result of attempt with {@link BSocket_GetConnectResult}.
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_Connect (BSocket *bs, BAddr *addr, int later) WARN_UNUSED;
-
-/**
- * Retreives the result of a connection attempt.
- * The socket must have completed a connection attempt whose result has not yet been retrieved.
- *
- * @param bs the object
- * @return connection attempt result. Possible values:
- *             - 0 connection successful
- *             - BSOCKET_ERROR_CONNECTION_TIMED_OUT timeout while attempting connection
- *             - BSOCKET_ERROR_CONNECTION_REFUSED no one is listening on the remote address
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_GetConnectResult (BSocket *bs);
-
-/**
- * Binds the socket to the specified address.
- *
- * @param bs the object
- * @param addr local address. Must not be an invalid address.
- * @return 0 for success,
- *         -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_ADDRESS_NOT_AVAILABLE the address is not a local address
- *             - BSOCKET_ERROR_ADDRESS_IN_USE the address is already in use
- *             - BSOCKET_ERROR_ACCESS_DENIED the address is protected
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_Bind (BSocket *bs, BAddr *addr) WARN_UNUSED;
-
-/**
- * Marks the socket as a listening socket.
- *
- * @param bs the object. Must be a BSOCKET_TYPE_STREAM socket.
- * @param backlog whatever this means in the system's listen() function. If it's
- *                negative, BSOCKET_DEFAULT_BACKLOG will be used.
- * @return 0 for success,
- *         -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_ADDRESS_IN_USE the address is already in use
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_Listen (BSocket *bs, int backlog) WARN_UNUSED;
-
-/**
- * Accepts a connection on a listening socket.
- *
- * @param bs the object. Must be a BSOCKET_TYPE_STREAM socket.
- * @param newsock on success, the new socket will be stored here. If it is NULL and a connection
- *                was accepted, it is closed immediately (but the function succeeds). The resulting
- *                socket will have the same domain and type as the listening socket.
- * @param addr if not NULL, the client address will be stored here on success.
- *             The returned address may be an invalid address.
- * @return 0 for success,
- *         -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_LATER a connection cannot be accepted at the moment
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr) WARN_UNUSED;
-
-/**
- * Sends data on a stream socket.
- *
- * @param bs the object. Must be a BSOCKET_TYPE_STREAM socket.
- * @param data buffer to read data from
- * @param len amount of data. Must be >=0.
- * @return non-negative value for amount of data sent,
- *         -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_LATER no data can be sent at the moment
- *             - BSOCKET_ERROR_CONNECTION_REFUSED the remote host refused to allow the network connection.
- *                   For UDP sockets, this means the remote sent an ICMP Port Unreachable packet.
- *             - BSOCKET_ERROR_CONNECTION_RESET connection was reset by the remote peer
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_Send (BSocket *bs, uint8_t *data, int len) WARN_UNUSED;
-
-/**
- * Receives data on a stream socket.
- *
- * @param bs the object. Must be a BSOCKET_TYPE_STREAM socket.
- * @param data buffer to write data to
- * @param len maximum amount of data to read. Must be >=0.
- * @return - non-negative value for amount of data read; on stream sockets the value 0
- *           means that the peer has shutdown the connection gracefully
- *         - -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_LATER no data can be read at the moment
- *             - BSOCKET_ERROR_CONNECTION_REFUSED the remote host refused to allow the network connection.
- *                   For UDP sockets, this means the remote sent an ICMP Port Unreachable packet.
- *             - BSOCKET_ERROR_CONNECTION_RESET connection was reset by the remote peer
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_Recv (BSocket *bs, uint8_t *data, int len) WARN_UNUSED;
-
-/**
- * Sends a datagram on a datagram socket to the specified address
- * from the specified local source address.
- *
- * @param bs the object. Must be a BSOCKET_TYPE_DGRAM socket.
- * @param data buffer to read data from
- * @param len amount of data. Must be >=0.
- * @param addr remote address. Must be valid.
- * @param local_addr source address. Must not be NULL, but may be invalid.
- * @return non-negative value for amount of data sent,
- *         -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_LATER no data can be sent at the moment
- *             - BSOCKET_ERROR_CONNECTION_REFUSED the remote host refused to allow the network connection.
- *                   For UDP sockets, this means the remote sent an ICMP Port Unreachable packet.
- *             - BSOCKET_ERROR_CONNECTION_RESET connection was reset by the remote peer
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_SendToFrom (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAddr *local_addr) WARN_UNUSED;
-
-/**
- * Receives a datagram on a datagram socket and returns the sender address
- * and the local destination address.
- *
- * @param bs the object. Must be a BSOCKET_TYPE_DGRAM socket.
- * @param data buffer to write data to
- * @param len maximum amount of data to read. Must be >=0.
- * @param addr the sender address will be stored here on success. Must not be NULL.
- *             The returned address may be an invalid address.
- * @param local_addr the destination address will be stored here on success. Must not be NULL.
- *                   Returned address will be invalid if it could not be determined.
- * @return - non-negative value for amount of data read; on stream sockets the value 0
- *           means that the peer has shutdown the connection gracefully
- *         - -1 for failure, where the error code can be:
- *             - BSOCKET_ERROR_LATER no data can be read at the moment
- *             - BSOCKET_ERROR_CONNECTION_REFUSED a remote host refused to allow the network connection.
- *                   For UDP sockets, this means the remote sent an ICMP Port Unreachable packet.
- *             - BSOCKET_ERROR_CONNECTION_RESET connection was reset by the remote peer
- *             - BSOCKET_ERROR_UNKNOWN unhandled error
- */
-int BSocket_RecvFromTo (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAddr *local_addr) WARN_UNUSED;
-
-/**
- * Returns the address of the remote peer.
- *
- * @param bs the object
- * @param addr where to store address. Must not be NULL.
- *             The returned address may be an invalid address.
- * @return 0 for success, -1 for failure
- */
-int BSocket_GetPeerName (BSocket *bs, BAddr *addr) WARN_UNUSED;
-
-#ifndef BADVPN_USE_WINAPI
-
-/**
- * Binds the unix socket to the specified path.
- *
- * @param bs the object
- * @param path path to bind to
- * @return 0 for success, -1 for failure
- */
-int BSocket_BindUnix (BSocket *bs, const char *path) WARN_UNUSED;
-
-/**
- * Connects the unix socket to the specified path.
- *
- * @param bs the object
- * @param path path to connect to
- * @return 0 for success, -1 for failure
- */
-int BSocket_ConnectUnix (BSocket *bs, const char *path) WARN_UNUSED;
-
-#endif
-
-/**
- * Returns the {@link BReactor} of this socket.
- * 
- * @param bs the object
- * @return {@link BReactor} of this socket
- */
-BReactor * BSocket_Reactor (BSocket *bs);
-
-/**
- * Returns the underlying file descriptor of the socket.
- * 
- * @param bs the object
- * @return file descriptor
- */
-int BSocket_SockFd (BSocket *bs);
-
-/**
- * Sets the socket's send buffer (SO_SNDBUF).
- * 
- * @param bs the object
- * @param buf_size buffer size in bytes. Must be >0.
- * @return 0 for success, -1 for failure
- */
-int BSocket_SetSendBuffer (BSocket *bs, int buf_size);
-
-#endif

+ 10 - 4
system/CMakeLists.txt

@@ -1,22 +1,28 @@
 set(BSYSTEM_ADDITIONAL_LIBS)
+set(BSYSTEM_ADDITIONAL_SOURCES)
+
 if (WIN32)
-    list(APPEND BSYSTEM_ADDITIONAL_LIBS ws2_32)
+    list(APPEND BSYSTEM_ADDITIONAL_LIBS ws2_32 mswsock)
+    list(APPEND BSYSTEM_ADDITIONAL_SOURCES
+        BConnection_win.c
+        BDatagram_win.c
+    )
 endif ()
 
-set(BSYSTEM_ADDITIONAL_SOURCES)
 if (NOT WIN32)
     list(APPEND BSYSTEM_ADDITIONAL_SOURCES
         BLog_syslog.c
         BUnixSignal.c
+        BConnection_unix.c
+        BDatagram_unix.c
     )
 endif ()
 
 add_library(system
     BReactor.c
     BSignal.c
-    BSocket.c
     BTime.c
-    Listener.c
+    BNetwork.c
     ${BSYSTEM_ADDITIONAL_SOURCES}
 )
 target_link_libraries(system base ${BSYSTEM_ADDITIONAL_LIBS})

+ 0 - 157
system/Listener.c

@@ -1,157 +0,0 @@
-/**
- * @file Listener.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 <stddef.h>
-
-#include <misc/debug.h>
-#include <base/BLog.h>
-
-#include <system/Listener.h>
-
-#include <generated/blog_channel_Listener.h>
-
-static void socket_handler (Listener *o, int event)
-{
-    ASSERT(event == BSOCKET_ACCEPT)
-    DebugObject_Access(&o->d_obj);
-    
-    // schedule accept job (to accept and close the connection in case the handler doesn't)
-    BPending_Set(&o->accept_job);
-    
-    // call handler
-    o->handler(o->user);
-    return;
-}
-
-static void accept_job_handler (Listener *o)
-{
-    DebugObject_Access(&o->d_obj);
-    
-    // accept and discard the connection
-    if (BSocket_Accept(o->sock, NULL, NULL) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Accept failed (%d)", BSocket_GetError(o->sock));
-    }
-}
-
-int Listener_Init (Listener *o, BReactor *reactor, BAddr addr, Listener_handler handler, void *user)
-{
-    ASSERT(!BAddr_IsInvalid(&addr))
-    
-    // init arguments
-    o->reactor = reactor;
-    o->handler = handler;
-    o->user = user;
-    
-    // set not existing
-    o->existing = 0;
-    
-    // create socket
-    if (BSocket_Init(&o->our_sock, o->reactor, addr.type, BSOCKET_TYPE_STREAM) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Init failed");
-        goto fail0;
-    }
-    
-    // set socket
-    o->sock = &o->our_sock;
-    
-    // bind socket
-    if (BSocket_Bind(o->sock, &addr) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Bind failed (%d)", BSocket_GetError(o->sock));
-        goto fail1;
-    }
-    
-    // listen socket
-    if (BSocket_Listen(o->sock, -1) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Listen failed (%d)", BSocket_GetError(o->sock));
-        goto fail1;
-    }
-    
-    // register socket event handler
-    BSocket_AddEventHandler(o->sock, BSOCKET_ACCEPT, (BSocket_handler)socket_handler, o);
-    BSocket_EnableEvent(o->sock, BSOCKET_ACCEPT);
-    
-    // init accept job
-    BPending_Init(&o->accept_job, BReactor_PendingGroup(o->reactor), (BPending_handler)accept_job_handler, o);
-    
-    DebugObject_Init(&o->d_obj);
-    
-    return 1;
-    
-fail1:
-    BSocket_Free(&o->our_sock);
-fail0:
-    return 0;
-}
-
-void Listener_InitExisting (Listener *o, BReactor *reactor, BSocket *sock, Listener_handler handler, void *user)
-{
-    // init arguments
-    o->reactor = reactor;
-    o->handler = handler;
-    o->user = user;
-    o->sock = sock;
-    
-    // set existing
-    o->existing = 1;
-    
-    // register socket event handler
-    BSocket_AddEventHandler(o->sock, BSOCKET_ACCEPT, (BSocket_handler)socket_handler, o);
-    BSocket_EnableEvent(o->sock, BSOCKET_ACCEPT);
-    
-    // init accept job
-    BPending_Init(&o->accept_job, BReactor_PendingGroup(o->reactor), (BPending_handler)accept_job_handler, o);
-    
-    DebugObject_Init(&o->d_obj);
-}
-
-void Listener_Free (Listener *o)
-{
-    DebugObject_Free(&o->d_obj);
-    
-    // free accept job
-    BPending_Free(&o->accept_job);
-    
-    // remove socket event handler
-    BSocket_RemoveEventHandler(o->sock, BSOCKET_ACCEPT);
-    
-    if (!o->existing) {
-        // free socket
-        BSocket_Free(&o->our_sock);
-    }
-}
-
-int Listener_Accept (Listener *o, BSocket *sockout, BAddr *addrout)
-{
-    ASSERT(sockout)
-    ASSERT(BPending_IsSet(&o->accept_job))
-    DebugObject_Access(&o->d_obj);
-    
-    // unset accept job
-    BPending_Unset(&o->accept_job);
-    
-    if (BSocket_Accept(o->sock, sockout, addrout) < 0) {
-        BLog(BLOG_ERROR, "BSocket_Accept failed (%d)", BSocket_GetError(o->sock));
-        return 0;
-    }
-    
-    return 1;
-}

+ 0 - 104
system/Listener.h

@@ -1,104 +0,0 @@
-/**
- * @file Listener.h
- * @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.
- * 
- * @section DESCRIPTION
- * 
- * Object used to listen on a socket and accept clients.
- */
-
-#ifndef BADVPN_SYSTEM_LISTENER_H
-#define BADVPN_SYSTEM_LISTENER_H
-
-#include <base/DebugObject.h>
-#include <system/BSocket.h>
-
-/**
- * Handler function called when it may be possible to accept a client.
- * The user can call {@link Listener_Accept} from this handler to accept
- * clients.
- * If the user does not call {@link Listener_Accept}, a newly connected
- * client may be disconnected.
- * 
- * @param user as in {@link Listener_Init}
- */
-typedef void (*Listener_handler) (void *user);
-
-/**
- * Object used to listen on a socket and accept clients.
- */
-typedef struct {
-    BReactor *reactor;
-    int existing;
-    BSocket our_sock;
-    BSocket *sock;
-    Listener_handler handler;
-    void *user;
-    BPending accept_job;
-    DebugObject d_obj;
-} Listener;
-
-/**
- * Initializes the object.
- * 
- * @param o the object
- * @param reactor reactor we live in
- * @param addr address to listen on. Must not be invalid.
- * @param handler handler function called when a connection should be accepted
- * @param user value to pass to handler function
- * @return 1 on success, 0 on failure
- */
-int Listener_Init (Listener *o, BReactor *reactor, BAddr addr, Listener_handler handler, void *user) WARN_UNUSED;
-
-/**
- * Initializes the object for listening on an existing socket.
- * The socket should be already bound and listened.
- * 
- * @param o the object
- * @param reactor reactor we live in
- * @param sock socket to listen on
- * @param handler handler function called when a connection should be accepted
- * @param user value to pass to handler function
- * @return 1 on success, 0 on failure
- */
-void Listener_InitExisting (Listener *o, BReactor *reactor, BSocket *sock, Listener_handler handler, void *user);
-
-/**
- * Frees the object.
- * 
- * @param o the object
- */
-void Listener_Free (Listener *o);
-
-/**
- * Accepts a connection.
- * Must be called from within the {@link Listener_handler} handler or its jobs, and
- * at most once.
- * 
- * @param o the object
- * @param sockout uninitialized {@link BSocket} structure to put the new socket in.
- *                Must not be NULL.
- * @param addrout if not NULL, the address of the client will be returned here
- *                on success
- * @return 1 on success, 0 on failure
- */
-int Listener_Accept (Listener *o, BSocket *sockout, BAddr *addrout) WARN_UNUSED;
-
-#endif

+ 4 - 4
tun2socks/tun2socks.c

@@ -37,9 +37,9 @@
 #include <structure/LinkedList2.h>
 #include <base/BLog.h>
 #include <system/BReactor.h>
-#include <system/BSocket.h>
 #include <system/BSignal.h>
 #include <system/BAddr.h>
+#include <system/BNetwork.h>
 #include <flow/PacketBuffer.h>
 #include <flow/BufferWriter.h>
 #include <flow/SinglePacketBuffer.h>
@@ -256,9 +256,9 @@ int main (int argc, char **argv)
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
-    // initialize sockets
-    if (BSocket_GlobalInit() < 0) {
-        BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
+    // initialize network
+    if (!BNetwork_GlobalInit()) {
+        BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
         goto fail1;
     }
     

+ 76 - 194
tuntap/BTap.c

@@ -61,140 +61,25 @@ static void output_handler_recv (BTap *o, uint8_t *data);
 
 #ifdef BADVPN_USE_WINAPI
 
-static int try_send (BTap *o, uint8_t *data, int data_len)
+static void recv_olap_handler (BTap *o, int event, DWORD bytes)
 {
-    // setup overlapped
-    memset(&o->input_ol, 0, sizeof(o->input_ol));
-    o->input_ol.hEvent = o->input_event;
-    
-    // attempt write
-    if (!WriteFile(o->device, data, data_len, NULL, &o->input_ol)) {
-        DWORD error = GetLastError();
-        if (error == ERROR_IO_PENDING) {
-            // write pending
-            return 0;
-        }
-        BLog(BLOG_WARNING, "WriteFile failed (%u)", error);
-        return 1;
-    }
-    
-    // read result
-    DWORD bytes;
-    if (!GetOverlappedResult(o->device, &o->input_ol, &bytes, FALSE)) {
-        BLog(BLOG_WARNING, "GetOverlappedResult failed (%u)", GetLastError());
-    }
-    else if (bytes != data_len) {
-        BLog(BLOG_WARNING, "written %d expected %d", (int)bytes, data_len);
-    }
-    
-    // reset event
-    ASSERT_FORCE(ResetEvent(o->input_event))
-    
-    return 1;
-}
-
-static int try_recv (BTap *o, uint8_t *data, int *data_len)
-{
-    // setup overlapped
-    memset(&o->output_ol, 0, sizeof(o->output_ol));
-    o->output_ol.hEvent = o->output_event;
-    
-    // attempt read
-    if (!ReadFile(o->device, data, o->frame_mtu, NULL, &o->output_ol)) {
-        DWORD error = GetLastError();
-        if (error == ERROR_IO_PENDING) {
-            // read pending
-            return 0;
-        }
-        
-        BLog(BLOG_ERROR, "ReadFile failed (%u)", error);
-        
-        // fatal error
-        return -1;
-    }
-    
-    // read result
-    DWORD bytes;
-    if (!GetOverlappedResult(o->device, &o->output_ol, &bytes, FALSE)) {
-        BLog(BLOG_ERROR, "GetOverlappedResult (output) failed (%u)", GetLastError());
-        
-        // fatal error
-        return -1;
-    }
-    
-    ASSERT_FORCE(bytes <= o->frame_mtu)
-    
-    // reset event
-    ASSERT_FORCE(ResetEvent(o->output_event))
-    
-    *data_len = bytes;
-    return 1;
-}
-
-static void write_handle_handler (BTap *o)
-{
-    ASSERT(o->input_packet_len >= 0)
-    DebugError_AssertNoError(&o->d_err);
     DebugObject_Access(&o->d_obj);
-    
-    // disable handle event
-    BReactor_DisableHandle(o->reactor, &o->input_bhandle);
-    
-    // read result
-    DWORD bytes;
-    if (!GetOverlappedResult(o->device, &o->input_ol, &bytes, FALSE)) {
-        BLog(BLOG_WARNING, "GetOverlappedResult (input) failed (%u)", GetLastError());
-    } else if (bytes != o->input_packet_len) {
-        BLog(BLOG_WARNING, "written %d expected %d", (int)bytes, o->input_packet_len);
-    }
-    
-    // set no input packet
-    o->input_packet_len = -1;
-    
-    // reset event
-    ASSERT_FORCE(ResetEvent(o->input_event))
-    
-    // inform sender we finished the packet
-    PacketPassInterface_Done(&o->input);
-}
-
-static void read_handle_handler (BTap *o)
-{
     ASSERT(o->output_packet)
-    DebugError_AssertNoError(&o->d_err);
-    DebugObject_Access(&o->d_obj);
+    ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
     
-    int bytes;
+    // set no output packet
+    o->output_packet = NULL;
     
-    // read result
-    DWORD dbytes;
-    if (!GetOverlappedResult(o->device, &o->output_ol, &dbytes, FALSE)) {
-        DWORD error = GetLastError();
-        BLog(BLOG_ERROR, "GetOverlappedResult (output) failed (%u)", error);
-        
-        // set no output packet (so that BTap_Free doesn't try getting the result again)
-        o->output_packet = NULL;
-        
-        // report fatal error
+    if (event == BREACTOR_IOCP_EVENT_FAILED) {
+        BLog(BLOG_ERROR, "read operation failed");
         report_error(o);
         return;
     }
     
-    // reset event
-    ASSERT_FORCE(ResetEvent(o->output_event))
-    
-    bytes = dbytes;
+    ASSERT(bytes >= 0)
+    ASSERT(bytes <= o->frame_mtu)
     
-done:
-    ASSERT_FORCE(bytes <= o->frame_mtu)
-    
-    // disable handle event
-    BReactor_DisableHandle(o->reactor, &o->output_bhandle);
-    
-    // set no output packet
-    o->output_packet = NULL;
-    
-    // inform receiver we finished the packet
+    // done
     PacketRecvInterface_Done(&o->output, bytes);
 }
 
@@ -284,13 +169,32 @@ void input_handler_send (BTap *o, uint8_t *data, int data_len)
     #ifdef BADVPN_USE_WINAPI
     
     // ignore frames without an Ethernet header, or we get errors in WriteFile
-    if (data_len >= 14) {
-        if (!try_send(o, data, data_len)) {
-            // write pending
-            o->input_packet = data;
-            o->input_packet_len = data_len;
-            BReactor_EnableHandle(o->reactor, &o->input_bhandle);
-            return;
+    if (data_len < 14) {
+        goto accept;
+    }
+    
+    memset(&o->send_olap.olap, 0, sizeof(o->send_olap.olap));
+    
+    // write
+    BOOL res = WriteFile(o->device, data, data_len, NULL, &o->send_olap.olap);
+    if (res == FALSE && GetLastError() != ERROR_IO_PENDING) {
+        BLog(BLOG_ERROR, "WriteFile failed (%u)", GetLastError());
+        goto accept;
+    }
+    
+    // wait
+    int succeeded;
+    DWORD bytes;
+    BReactorIOCPOverlapped_Wait(&o->send_olap, &succeeded, &bytes);
+    
+    if (!succeeded) {
+        BLog(BLOG_ERROR, "write operation failed");
+    } else {
+        ASSERT(bytes >= 0)
+        ASSERT(bytes <= data_len)
+        
+        if (bytes < data_len) {
+            BLog(BLOG_ERROR, "write operation didn't write everything");
         }
     }
     
@@ -318,6 +222,7 @@ void input_handler_send (BTap *o, uint8_t *data, int data_len)
     
     #endif
     
+accept:
     PacketPassInterface_Done(&o->input);
 }
 
@@ -328,23 +233,21 @@ void output_handler_recv (BTap *o, uint8_t *data)
     DebugError_AssertNoError(&o->d_err);
     DebugObject_Access(&o->d_obj);
     
-    #ifdef BADVPN_USE_WINAPI
+#ifdef BADVPN_USE_WINAPI
     
-    int bytes;
-    int res;
-    if ((res = try_recv(o, data, &bytes)) < 0) {
-        // report fatal error
+    memset(&o->recv_olap.olap, 0, sizeof(o->recv_olap.olap));
+    
+    // read
+    BOOL res = ReadFile(o->device, data, o->frame_mtu, NULL, &o->recv_olap.olap);
+    if (res == FALSE && GetLastError() != ERROR_IO_PENDING) {
+        BLog(BLOG_ERROR, "ReadFile failed (%u)", GetLastError());
         report_error(o);
         return;
     }
-    if (!res) {
-        // read pending
-        o->output_packet = data;
-        BReactor_EnableHandle(o->reactor, &o->output_bhandle);
-        return;
-    }
     
-    #else
+    o->output_packet = data;
+    
+#else
     
     // attempt read
     int bytes = read(o->fd, data, o->frame_mtu);
@@ -363,11 +266,11 @@ void output_handler_recv (BTap *o, uint8_t *data)
         return;
     }
     
-    #endif
-    
     ASSERT_FORCE(bytes <= o->frame_mtu)
     
     PacketRecvInterface_Done(&o->output, bytes);
+    
+#endif
 }
 
 int BTap_Init (BTap *o, BReactor *reactor, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun)
@@ -463,38 +366,21 @@ int BTap_Init (BTap *o, BReactor *reactor, char *devname, BTap_handler_error han
     
     BLog(BLOG_INFO, "Device opened");
     
-    // init input/output
+    // associate device with IOCP
     
-    if (!(o->input_event = CreateEvent(NULL, TRUE, FALSE, NULL))) {
-        BLog(BLOG_ERROR, "CreateEvent failed");
+    if (!CreateIoCompletionPort(o->device, BReactor_GetIOCPHandle(o->reactor), 0, 0)) {
+        BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
         goto fail1;
     }
     
-    if (!(o->output_event = CreateEvent(NULL, TRUE, FALSE, NULL))) {
-        BLog(BLOG_ERROR, "CreateEvent failed");
-        goto fail2;
-    }
-    
-    BHandle_Init(&o->input_bhandle, o->input_event, (BHandle_handler)write_handle_handler, o);
-    BHandle_Init(&o->output_bhandle, o->output_event, (BHandle_handler)read_handle_handler, o);
+    // init send olap
+    BReactorIOCPOverlapped_Init(&o->send_olap, o->reactor, o, NULL);
     
-    if (!BReactor_AddHandle(o->reactor, &o->input_bhandle)) {
-        BLog(BLOG_ERROR, "BReactor_AddHandle failed");
-        goto fail3;
-    }
-    if (!BReactor_AddHandle(o->reactor, &o->output_bhandle)) {
-        BLog(BLOG_ERROR, "BReactor_AddHandle failed");
-        goto fail4;
-    }
+    // init recv olap
+    BReactorIOCPOverlapped_Init(&o->recv_olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler);
     
     goto success;
     
-fail4:
-    BReactor_RemoveHandle(o->reactor, &o->input_bhandle);
-fail3:
-    ASSERT_FORCE(CloseHandle(o->output_event))
-fail2:
-    ASSERT_FORCE(CloseHandle(o->input_event))
 fail1:
     ASSERT_FORCE(CloseHandle(o->device))
 fail0:
@@ -650,37 +536,33 @@ void BTap_Free (BTap *o)
     // free input
     PacketPassInterface_Free(&o->input);
     
-    #ifdef BADVPN_USE_WINAPI
+#ifdef BADVPN_USE_WINAPI
     
-    // wait for pending i/o
+    // cancel I/O
     ASSERT_FORCE(CancelIo(o->device))
-    DWORD bytes;
-    DWORD error;
-    if (o->input_packet_len >= 0) {
-        if (!GetOverlappedResult(o->device, &o->input_ol, &bytes, TRUE)) {
-            error = GetLastError();
-            if (error != ERROR_OPERATION_ABORTED) {
-                BLog(BLOG_WARNING, "GetOverlappedResult (input) failed (%u)", error);
-            }
-        }
-    }
+    
+    // wait receiving to finish
     if (o->output_packet) {
-        if (!GetOverlappedResult(o->device, &o->output_ol, &bytes, TRUE)) {
-            error = GetLastError();
-            if (error != ERROR_OPERATION_ABORTED) {
-                BLog(BLOG_WARNING, "GetOverlappedResult (output) failed (%u)", error);
-            }
-        }
+        BLog(BLOG_DEBUG, "waiting for sending to finish");
+        BReactorIOCPOverlapped_Wait(&o->recv_olap, NULL, NULL);
+    }
+    
+    // wait sending to finish
+    if (o->input_packet_len >= 0) {
+        BLog(BLOG_DEBUG, "waiting for sending to finish");
+        BReactorIOCPOverlapped_Wait(&o->send_olap, NULL, NULL);
     }
     
-    // free stuff
-    BReactor_RemoveHandle(o->reactor, &o->input_bhandle);
-    BReactor_RemoveHandle(o->reactor, &o->output_bhandle);
-    ASSERT_FORCE(CloseHandle(o->output_event))
-    ASSERT_FORCE(CloseHandle(o->input_event))
+    // free recv olap
+    BReactorIOCPOverlapped_Free(&o->recv_olap);
+    
+    // free send olap
+    BReactorIOCPOverlapped_Free(&o->send_olap);
+    
+    // close device
     ASSERT_FORCE(CloseHandle(o->device))
     
-    #else
+#else
     
     // free BFileDescriptor
     BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
@@ -688,7 +570,7 @@ void BTap_Free (BTap *o)
     // close file descriptor
     ASSERT_FORCE(close(o->fd) == 0)
     
-    #endif
+#endif
 }
 
 int BTap_GetMTU (BTap *o)

+ 5 - 9
tuntap/BTap.h

@@ -66,20 +66,16 @@ typedef struct {
     int input_packet_len;
     uint8_t *output_packet;
     
-    #ifdef BADVPN_USE_WINAPI
+#ifdef BADVPN_USE_WINAPI
     HANDLE device;
-    HANDLE input_event;
-    HANDLE output_event;
-    BHandle input_bhandle;
-    BHandle output_bhandle;
-    OVERLAPPED input_ol;
-    OVERLAPPED output_ol;
-    #else
+    BReactorIOCPOverlapped send_olap;
+    BReactorIOCPOverlapped recv_olap;
+#else
     int fd;
     BFileDescriptor bfd;
     char devname[IFNAMSIZ];
     int poll_events;
-    #endif
+#endif
     
     DebugObject d_obj;
     DebugError d_err;

+ 71 - 85
udpgw/udpgw.c

@@ -38,18 +38,15 @@
 #include <structure/BAVL.h>
 #include <base/BLog.h>
 #include <system/BReactor.h>
-#include <system/BSocket.h>
+#include <system/BNetwork.h>
+#include <system/BConnection.h>
+#include <system/BDatagram.h>
 #include <system/BSignal.h>
-#include <system/Listener.h>
 #include <flow/PacketProtoDecoder.h>
 #include <flow/PacketPassFairQueue.h>
 #include <flow/PacketStreamSender.h>
 #include <flow/PacketProtoFlow.h>
 #include <flow/SinglePacketBuffer.h>
-#include <flowextra/StreamSocketSource.h>
-#include <flowextra/StreamSocketSink.h>
-#include <flowextra/DatagramSocketSource.h>
-#include <flowextra/DatagramSocketSink.h>
 
 #ifndef BADVPN_USE_WINAPI
 #include <system/BLog_syslog.h>
@@ -63,16 +60,14 @@
 #define LOGGER_SYSLOG 2
 
 struct client {
-    BSocket sock;
+    BConnection con;
     BAddr addr;
     BTimer disconnect_timer;
-    FlowErrorDomain domain;
-    StreamSocketSource recv_source;
+    FlowErrorDomain recv_decoder_domain;
     PacketProtoDecoder recv_decoder;
     PacketPassInterface recv_if;
     PacketPassFairQueue send_queue;
     PacketStreamSender send_sender;
-    StreamSocketSink send_sink;
     BAVL connections_tree;
     LinkedList1 connections_list;
     int num_connections;
@@ -93,12 +88,10 @@ struct connection {
     PacketPassFairQueueFlow send_qflow;
     union {
         struct {
-            BSocket udp_sock;
+            BDatagram udp_dgram;
             FlowErrorDomain udp_domain;
             BufferWriter udp_send_writer;
             PacketBuffer udp_send_buffer;
-            DatagramSocketSink udp_send_sink;
-            DatagramSocketSource udp_recv_source;
             SinglePacketBuffer udp_recv_buffer;
             PacketPassInterface udp_recv_if;
             BAVLNode connections_tree_node;
@@ -140,7 +133,7 @@ int num_listen_addrs;
 BReactor ss;
 
 // listeners
-Listener listeners[MAX_LISTEN_ADDRS];
+BListener listeners[MAX_LISTEN_ADDRS];
 int num_listeners;
 
 // clients
@@ -152,11 +145,12 @@ static void print_version (void);
 static int parse_arguments (int argc, char *argv[]);
 static int process_arguments (void);
 static void signal_handler (void *unused);
-static void listener_handler (Listener *listener);
+static void listener_handler (BListener *listener);
 static void client_free (struct client *client);
 static void client_log (struct client *client, int level, const char *fmt, ...);
 static void client_disconnect_timer_handler (struct client *client);
-static void client_error_handler (struct client *client, int component, int code);
+static void client_connection_handler (struct client *client, int event);
+static void client_decoder_handler (struct client *client, int component, int code);
 static void client_recv_if_handler_send (struct client *client, uint8_t *data, int data_len);
 static void connection_init (struct client *client, uint16_t conid, BAddr addr, const uint8_t *data, int data_len);
 static void connection_free (struct connection *con);
@@ -167,7 +161,7 @@ static int connection_send_to_client (struct connection *con, uint8_t flags, con
 static int connection_send_to_udp (struct connection *con, const uint8_t *data, int data_len);
 static void connection_close (struct connection *con);
 static void connection_send_qflow_busy_handler (struct connection *con);
-static void connection_udp_error_handler (struct connection *con, int component, int code);
+static void connection_dgram_handler_event (struct connection *con, int event);
 static void connection_udp_recv_if_handler_send (struct connection *con, uint8_t *data, int data_len);
 static struct connection * find_connection (struct client *client, uint16_t conid);
 static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2);
@@ -225,9 +219,9 @@ int main (int argc, char **argv)
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
-    // initialize sockets
-    if (BSocket_GlobalInit() < 0) {
-        BLog(BLOG_ERROR, "BSocket_GlobalInit failed");
+    // initialize network
+    if (!BNetwork_GlobalInit()) {
+        BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
         goto fail1;
     }
     
@@ -264,7 +258,7 @@ int main (int argc, char **argv)
     // initialize listeners
     num_listeners = 0;
     while (num_listeners < num_listen_addrs) {
-        if (!Listener_Init(&listeners[num_listeners], &ss, listen_addrs[num_listeners], (Listener_handler)listener_handler, &listeners[num_listeners])) {
+        if (!BListener_Init(&listeners[num_listeners], listen_addrs[num_listeners], &ss, &listeners[num_listeners], (BListener_handler)listener_handler)) {
             BLog(BLOG_ERROR, "Listener_Init failed");
             goto fail3;
         }
@@ -288,7 +282,7 @@ fail3:
     // free listeners
     while (num_listeners > 0) {
         num_listeners--;
-        Listener_Free(&listeners[num_listeners]);
+        BListener_Free(&listeners[num_listeners]);
     }
     // finish signal handling
     BSignal_Finish();
@@ -516,7 +510,7 @@ void signal_handler (void *unused)
     BReactor_Quit(&ss, 1);
 }
 
-void listener_handler (Listener *listener)
+void listener_handler (BListener *listener)
 {
     if (num_clients == options.max_clients) {
         BLog(BLOG_ERROR, "maximum number of clients reached");
@@ -531,40 +525,36 @@ void listener_handler (Listener *listener)
     }
     
     // accept client
-    if (!Listener_Accept(listener, &client->sock, &client->addr)) {
-        BLog(BLOG_ERROR, "Listener_Accept failed");
+    if (!BConnection_Init(&client->con, BCONNECTION_SOURCE_LISTENER(listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) {
+        BLog(BLOG_ERROR, "BConnection_Init failed");
         goto fail1;
     }
     
     // limit socket send buffer, else our scheduling is pointless
-    if (BSocket_SetSendBuffer(&client->sock, CLIENT_SOCKET_SEND_BUFFER) < 0) {
-        BLog(BLOG_WARNING, "BSocket_SetSendBuffer failed");
+    if (!BConnection_SetSendBuffer(&client->con, CLIENT_SOCKET_SEND_BUFFER)) {
+        BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed");
     }
     
+    // init connection interfaces
+    BConnection_SendAsync_Init(&client->con);
+    BConnection_RecvAsync_Init(&client->con);
+    
     // init disconnect timer
     BTimer_Init(&client->disconnect_timer, CLIENT_DISCONNECT_TIMEOUT, (BTimer_handler)client_disconnect_timer_handler, client);
     BReactor_SetTimer(&ss, &client->disconnect_timer);
     
-    // init error domain
-    FlowErrorDomain_Init(&client->domain, (FlowErrorDomain_handler)client_error_handler, client);
-    
-    // init recv source
-    StreamSocketSource_Init(&client->recv_source, FlowErrorReporter_Create(&client->domain, 0), &client->sock, BReactor_PendingGroup(&ss));
-    
     // init recv interface
     PacketPassInterface_Init(&client->recv_if, udpgw_mtu, (PacketPassInterface_handler_send)client_recv_if_handler_send, client, BReactor_PendingGroup(&ss));
     
     // init recv decoder
-    if (!PacketProtoDecoder_Init(&client->recv_decoder, FlowErrorReporter_Create(&client->domain, 0), StreamSocketSource_GetOutput(&client->recv_source), &client->recv_if, BReactor_PendingGroup(&ss))) {
+    FlowErrorDomain_Init(&client->recv_decoder_domain, (FlowErrorDomain_handler)client_decoder_handler, client);
+    if (!PacketProtoDecoder_Init(&client->recv_decoder, FlowErrorReporter_Create(&client->recv_decoder_domain, 0), BConnection_RecvAsync_GetIf(&client->con), &client->recv_if, BReactor_PendingGroup(&ss))) {
         BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed");
         goto fail2;
     }
     
-    // init send sink
-    StreamSocketSink_Init(&client->send_sink, FlowErrorReporter_Create(&client->domain, 0), &client->sock, BReactor_PendingGroup(&ss));
-    
     // init send sender
-    PacketStreamSender_Init(&client->send_sender, StreamSocketSink_GetInput(&client->send_sink), pp_mtu, BReactor_PendingGroup(&ss));
+    PacketStreamSender_Init(&client->send_sender, BConnection_SendAsync_GetIf(&client->con), pp_mtu, BReactor_PendingGroup(&ss));
     
     // init send queue
     PacketPassFairQueue_Init(&client->send_queue, PacketStreamSender_GetInput(&client->send_sender), BReactor_PendingGroup(&ss), 0, 1);
@@ -591,9 +581,10 @@ void listener_handler (Listener *listener)
     
 fail2:
     PacketPassInterface_Free(&client->recv_if);
-    StreamSocketSource_Free(&client->recv_source);
     BReactor_RemoveTimer(&ss, &client->disconnect_timer);
-    BSocket_Free(&client->sock);
+    BConnection_RecvAsync_Free(&client->con);
+    BConnection_SendAsync_Free(&client->con);
+    BConnection_Free(&client->con);
 fail1:
     free(client);
 fail0:
@@ -627,23 +618,21 @@ void client_free (struct client *client)
     // free send sender
     PacketStreamSender_Free(&client->send_sender);
     
-    // free send sink
-    StreamSocketSink_Free(&client->send_sink);
-    
     // free recv decoder
     PacketProtoDecoder_Free(&client->recv_decoder);
     
     // free recv interface
     PacketPassInterface_Free(&client->recv_if);
     
-    // free recv source
-    StreamSocketSource_Free(&client->recv_source);
-    
     // free disconnect timer
     BReactor_RemoveTimer(&ss, &client->disconnect_timer);
     
-    // free socket
-    BSocket_Free(&client->sock);
+    // free connection interfaces
+    BConnection_RecvAsync_Free(&client->con);
+    BConnection_SendAsync_Free(&client->con);
+    
+    // free connection
+    BConnection_Free(&client->con);
     
     // free structure
     free(client);
@@ -668,9 +657,21 @@ void client_disconnect_timer_handler (struct client *client)
     client_free(client);
 }
 
-void client_error_handler (struct client *client, int component, int code)
+void client_connection_handler (struct client *client, int event)
+{
+    if (event == BCONNECTION_EVENT_RECVCLOSED) {
+        client_log(client, BLOG_INFO, "client closed");
+    } else {
+        client_log(client, BLOG_INFO, "client error");
+    }
+    
+    // free client
+    client_free(client);
+}
+
+void client_decoder_handler (struct client *client, int component, int code)
 {
-    client_log(client, BLOG_INFO, "error");
+    client_log(client, BLOG_ERROR, "decoder error");
     
     // free client
     client_free(client);
@@ -783,47 +784,35 @@ void connection_init (struct client *client, uint16_t conid, BAddr addr, const u
     }
     con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow);
     
-    // init UDP socket
-    if (BSocket_Init(&con->udp_sock, &ss, addr.type, BSOCKET_TYPE_DGRAM) < 0) {
-        client_log(client, BLOG_ERROR, "BSocket_Init failed");
+    // init UDP dgram
+    if (!BDatagram_Init(&con->udp_dgram, addr.type, &ss, con, (BDatagram_handler)connection_dgram_handler_event)) {
+        client_log(client, BLOG_ERROR, "BDatagram_Init failed");
         goto fail2;
     }
     
-    // connect the socket
-    // Windows needs this or receive will fail; however, FreeBSD will refuse to send
-    // if this is done
-    #ifdef BADVPN_USE_WINAPI
-    if (BSocket_Connect(&con->udp_sock, &addr, 0) < 0) {
-        client_log(client, BLOG_ERROR, "BSocket_Connect failed");
-        goto fail3;
-    }
-    #endif
-    
-    // init UDP error domain
-    FlowErrorDomain_Init(&con->udp_domain, (FlowErrorDomain_handler)connection_udp_error_handler, con);
-    
-    // init UDP sink
+    // set UDP dgram send address
     BIPAddr ipaddr;
     BIPAddr_InitInvalid(&ipaddr);
-    DatagramSocketSink_Init(&con->udp_send_sink, FlowErrorReporter_Create(&con->udp_domain, 0), &con->udp_sock, options.udp_mtu, addr, ipaddr, BReactor_PendingGroup(&ss));
+    BDatagram_SetSendAddrs(&con->udp_dgram, addr, ipaddr);
+    
+    // init UDP dgram interfaces
+    BDatagram_SendAsync_Init(&con->udp_dgram, options.udp_mtu);
+    BDatagram_RecvAsync_Init(&con->udp_dgram, options.udp_mtu);
     
     // init UDP writer
     BufferWriter_Init(&con->udp_send_writer, options.udp_mtu, BReactor_PendingGroup(&ss));
     
     // init UDP buffer
-    if (!PacketBuffer_Init(&con->udp_send_buffer, BufferWriter_GetOutput(&con->udp_send_writer), DatagramSocketSink_GetInput(&con->udp_send_sink), CONNECTION_UDP_BUFFER_SIZE, BReactor_PendingGroup(&ss))) {
+    if (!PacketBuffer_Init(&con->udp_send_buffer, BufferWriter_GetOutput(&con->udp_send_writer), BDatagram_SendAsync_GetIf(&con->udp_dgram), CONNECTION_UDP_BUFFER_SIZE, BReactor_PendingGroup(&ss))) {
         client_log(client, BLOG_ERROR, "PacketBuffer_Init failed");
         goto fail4;
     }
     
-    // init UDP receive source
-    DatagramSocketSource_Init(&con->udp_recv_source, FlowErrorReporter_Create(&con->udp_domain, 0), &con->udp_sock, options.udp_mtu, BReactor_PendingGroup(&ss));
-    
     // init UDP recv interface
     PacketPassInterface_Init(&con->udp_recv_if, options.udp_mtu, (PacketPassInterface_handler_send)connection_udp_recv_if_handler_send, con, BReactor_PendingGroup(&ss));
     
     // init UDP recv buffer
-    if (!SinglePacketBuffer_Init(&con->udp_recv_buffer, DatagramSocketSource_GetOutput(&con->udp_recv_source), &con->udp_recv_if, BReactor_PendingGroup(&ss))) {
+    if (!SinglePacketBuffer_Init(&con->udp_recv_buffer, BDatagram_RecvAsync_GetIf(&con->udp_dgram), &con->udp_recv_if, BReactor_PendingGroup(&ss))) {
         client_log(client, BLOG_ERROR, "SinglePacketBuffer_Init failed");
         goto fail5;
     }
@@ -843,13 +832,12 @@ void connection_init (struct client *client, uint16_t conid, BAddr addr, const u
     
 fail5:
     PacketPassInterface_Free(&con->udp_recv_if);
-    DatagramSocketSource_Free(&con->udp_recv_source);
     PacketBuffer_Free(&con->udp_send_buffer);
 fail4:
     BufferWriter_Free(&con->udp_send_writer);
-    DatagramSocketSink_Free(&con->udp_send_sink);
-fail3:
-    BSocket_Free(&con->udp_sock);
+    BDatagram_RecvAsync_Free(&con->udp_dgram);
+    BDatagram_SendAsync_Free(&con->udp_dgram);
+    BDatagram_Free(&con->udp_dgram);
 fail2:
     PacketProtoFlow_Free(&con->send_ppflow);
 fail1:
@@ -918,20 +906,18 @@ void connection_free_udp (struct connection *con)
     // free UDP receive interface
     PacketPassInterface_Free(&con->udp_recv_if);
     
-    // free UDP receive source
-    DatagramSocketSource_Free(&con->udp_recv_source);
-    
     // free UDP buffer
     PacketBuffer_Free(&con->udp_send_buffer);
     
     // free UDP writer
     BufferWriter_Free(&con->udp_send_writer);
     
-    // free UDP sink
-    DatagramSocketSink_Free(&con->udp_send_sink);
+    // free UDP dgram interfaces
+    BDatagram_RecvAsync_Free(&con->udp_dgram);
+    BDatagram_SendAsync_Free(&con->udp_dgram);
     
-    // free UDP socket
-    BSocket_Free(&con->udp_sock);
+    // free UDP dgram
+    BDatagram_Free(&con->udp_dgram);
 }
 
 void connection_first_job_handler (struct connection *con)
@@ -1047,7 +1033,7 @@ void connection_send_qflow_busy_handler (struct connection *con)
     connection_free(con);
 }
 
-void connection_udp_error_handler (struct connection *con, int component, int code)
+void connection_dgram_handler_event (struct connection *con, int event)
 {
     ASSERT(!con->closing)