Explorar o código

StreamPeerIO: use flows to send the password. Add SinglePacketSender and PasswordSender for this.

ambrop7 %!s(int64=15) %!d(string=hai) anos
pai
achega
f670f63a18

+ 1 - 0
client/CMakeLists.txt

@@ -4,6 +4,7 @@ add_executable(badvpn-client
     DatagramPeerIO.c
     PasswordListener.c
     DataProto.c
+    PasswordSender.c
 )
 target_link_libraries(badvpn-client system flow tuntap server_conection listener ${LIBCRYPTO_LIBRARIES} ${NSPR_LIBRARIES} ${NSS_LIBRARIES})
 

+ 119 - 0
client/PasswordSender.c

@@ -0,0 +1,119 @@
+/**
+ * @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)
+{
+    #ifndef NDEBUG
+    DEAD_ENTER(o->dead)
+    #endif
+    
+    o->handler(o->user, is_error);
+    
+    #ifndef NDEBUG
+    ASSERT(DEAD_KILLED)
+    DEAD_LEAVE(o->dead);
+    #endif
+}
+
+static void error_handler (PasswordSender *o, int component, const void *data)
+{
+    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 dead var
+    DEAD_INIT(o->dead);
+    
+    // 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);
+        sink_if = PRStreamSink_GetInput(&o->sink.ssl);
+    } else {
+        StreamSocketSink_Init(&o->sink.plain, FlowErrorReporter_Create(&o->domain, COMPONENT_SINK), o->plain_sock);
+        sink_if = StreamSocketSink_GetInput(&o->sink.plain);
+    }
+    
+    // init PacketStreamSender
+    PacketStreamSender_Init(&o->pss, sink_if, sizeof(o->password));
+    
+    // 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);
+}
+
+void PasswordSender_Free (PasswordSender *o)
+{
+    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);
+    }
+    
+    // free dead var
+    DEAD_KILL(o->dead);
+}

+ 99 - 0
client/PasswordSender.h

@@ -0,0 +1,99 @@
+/**
+ * @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/dead.h>
+#include <system/BSocket.h>
+#include <system/DebugObject.h>
+#include <flow/SinglePacketSender.h>
+#include <flow/PacketStreamSender.h>
+#include <flow/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 {
+    dead_t dead;
+    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;
+} 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

+ 23 - 104
client/StreamPeerIO.c

@@ -50,7 +50,8 @@
 #define CONNECT_STATE_CONNECTING 0
 #define CONNECT_STATE_HANDSHAKE 1
 #define CONNECT_STATE_SENDING 2
-#define CONNECT_STATE_FINISHED 3
+#define CONNECT_STATE_SENT 3
+#define CONNECT_STATE_FINISHED 4
 
 #define LISTEN_STATE_LISTENER 0
 #define LISTEN_STATE_GOTCLIENT 1
@@ -63,10 +64,7 @@ static SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc
 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_try_send_ssl (StreamPeerIO *pio);
-static void connecting_try_send_plain (StreamPeerIO *pio);
-static void connecting_socket_write_handler_ssl (StreamPeerIO *pio, PRInt16 event);
-static void connecting_socket_write_handler_plain (StreamPeerIO *pio, int event);
+static void connecting_pwsender_handler (StreamPeerIO *pio, int is_error);
 static void error_handler (StreamPeerIO *pio, int component, const void *data);
 static void listener_handler_client (StreamPeerIO *pio, sslsocket *sock);
 static int init_io (StreamPeerIO *pio, sslsocket *sock);
@@ -136,17 +134,12 @@ void connecting_connect_handler (StreamPeerIO *pio, int event)
         connecting_try_handshake(pio);
         return;
     } else {
-        // add write handler
-        BSocket_AddEventHandler(&pio->connect.sock.sock, BSOCKET_WRITE, (BSocket_handler)connecting_socket_write_handler_plain, pio);
-        
-        // haven't sent anything yet
-        pio->connect.connecting_sending_sent = 0;
+        // 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);
         
         // change state
         pio->connect.state = CONNECT_STATE_SENDING;
         
-        // start sending password
-        connecting_try_send_plain(pio);
         return;
     }
     
@@ -243,17 +236,12 @@ void connecting_try_handshake (StreamPeerIO *pio)
     // remove read handler
     BPRFileDesc_RemoveEventHandler(&pio->connect.sock.ssl_bprfd, PR_POLL_READ);
     
-    // add write handler
-    BPRFileDesc_AddEventHandler(&pio->connect.sock.ssl_bprfd, PR_POLL_WRITE, (BPRFileDesc_handler)connecting_socket_write_handler_ssl, pio);
-    
-    // haven't send anything yet
-    pio->connect.connecting_sending_sent = 0;
+    // 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);
     
     // change state
     pio->connect.state = CONNECT_STATE_SENDING;
     
-    // start sending password
-    connecting_try_send_ssl(pio);
     return;
     
     // cleanup
@@ -270,77 +258,26 @@ void connecting_handshake_read_handler (StreamPeerIO *pio, PRInt16 event)
     return;
 }
 
-void connecting_try_send_ssl (StreamPeerIO *pio)
+static void connecting_pwsender_handler (StreamPeerIO *pio, int is_error)
 {
-    ASSERT(pio->ssl)
     ASSERT(pio->mode == MODE_CONNECT)
     ASSERT(pio->connect.state == CONNECT_STATE_SENDING)
     
-    while (pio->connect.connecting_sending_sent < sizeof(pio->connect.connecting_password)) {
-        PRInt32 sent = PR_Write(
-            pio->connect.sock.ssl_prfd,
-            (uint8_t *)&pio->connect.connecting_password + pio->connect.connecting_sending_sent,
-            sizeof(pio->connect.connecting_password) - pio->connect.connecting_sending_sent
-        );
-        if (sent < 0) {
-            PRErrorCode error = PR_GetError();
-            if (error == PR_WOULD_BLOCK_ERROR) {
-                BPRFileDesc_EnableEvent(&pio->connect.sock.ssl_bprfd, PR_POLL_WRITE);
-                return;
-            }
-            BLog(BLOG_NOTICE, "PR_Write failed (%d)", (int)error);
-            goto fail0;
+    if (is_error) {
+        BLog(BLOG_NOTICE, "error sending password");
+        BLog(BLOG_NOTICE,"BSocket error %d", BSocket_GetError(&pio->connect.sock.sock));
+        if (pio->ssl) {
+            BLog(BLOG_NOTICE, "NSPR error %d", (int)PR_GetError());
         }
-        pio->connect.connecting_sending_sent += sent;
-    }
-    
-    // remove write handler
-    BPRFileDesc_RemoveEventHandler(&pio->connect.sock.ssl_bprfd, PR_POLL_WRITE);
-    
-    // setup i/o
-    if (!init_io(pio, &pio->connect.sock)) {
+        
         goto fail0;
     }
     
-    // change state
-    pio->connect.state = CONNECT_STATE_FINISHED;
-    
-    return;
-    
-    // cleanup
-fail0:
-    reset_state(pio);
-    // report error
-    pio->handler_error(pio->user);
-    return;
-}
-
-void connecting_try_send_plain (StreamPeerIO *pio)
-{
-    ASSERT(!pio->ssl)
-    ASSERT(pio->mode == MODE_CONNECT)
-    ASSERT(pio->connect.state == CONNECT_STATE_SENDING)
-    
-    while (pio->connect.connecting_sending_sent < sizeof(pio->connect.connecting_password)) {
-        int sent = BSocket_Send(
-            &pio->connect.sock.sock,
-            (uint8_t *)&pio->connect.connecting_password + pio->connect.connecting_sending_sent,
-            sizeof(pio->connect.connecting_password) - pio->connect.connecting_sending_sent
-        );
-        if (sent < 0) {
-            int error = BSocket_GetError(&pio->connect.sock.sock);
-            if (error == BSOCKET_ERROR_LATER) {
-                BSocket_EnableEvent(&pio->connect.sock.sock, BSOCKET_WRITE);
-                return;
-            }
-            BLog(BLOG_NOTICE, "BSocket_Send failed (%d)", error);
-            goto fail0;
-        }
-        pio->connect.connecting_sending_sent += sent;
-    }
+    // free password sender
+    PasswordSender_Free(&pio->connect.pwsender);
     
-    // remove write event handler
-    BSocket_RemoveEventHandler(&pio->connect.sock.sock, BSOCKET_WRITE);
+    // change state
+    pio->connect.state = CONNECT_STATE_SENT;
     
     // setup i/o
     if (!init_io(pio, &pio->connect.sock)) {
@@ -352,7 +289,6 @@ void connecting_try_send_plain (StreamPeerIO *pio)
     
     return;
     
-    // cleanup
 fail0:
     reset_state(pio);
     // report error
@@ -360,27 +296,6 @@ fail0:
     return;
 }
 
-void connecting_socket_write_handler_ssl (StreamPeerIO *pio, PRInt16 event)
-{
-    ASSERT(event == PR_POLL_WRITE)
-    
-    // try to send data
-    connecting_try_send_ssl(pio);
-    return;
-}
-
-void connecting_socket_write_handler_plain (StreamPeerIO *pio, int event)
-{
-    ASSERT(event == BSOCKET_WRITE)
-    
-    // disable write event
-    BSocket_DisableEvent(&pio->connect.sock.sock, BSOCKET_WRITE);
-    
-    // try to send data
-    connecting_try_send_plain(pio);
-    return;
-}
-
 void error_handler (StreamPeerIO *pio, int component, const void *data)
 {
     ASSERT(pio->sock)
@@ -639,7 +554,11 @@ void reset_state (StreamPeerIO *pio)
             switch (pio->connect.state) {
                 case CONNECT_STATE_FINISHED:
                     free_io(pio);
+                case CONNECT_STATE_SENT:
                 case CONNECT_STATE_SENDING:
+                    if (pio->connect.state == CONNECT_STATE_SENDING) {
+                        PasswordSender_Free(&pio->connect.pwsender);
+                    }
                 case CONNECT_STATE_HANDSHAKE:
                     if (pio->ssl) {
                         BPRFileDesc_Free(&pio->connect.sock.ssl_bprfd);
@@ -769,7 +688,7 @@ int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERT
         pio->connect.ssl_cert = ssl_cert;
         pio->connect.ssl_key = ssl_key;
     }
-    pio->connect.connecting_password = htol64(password);
+    pio->connect.password = htol64(password);
     
     // set state
     pio->mode = MODE_CONNECT;

+ 3 - 3
client/StreamPeerIO.h

@@ -49,8 +49,8 @@
 #include <flow/StreamRecvConnector.h>
 #include <nspr_support/PRStreamSink.h>
 #include <nspr_support/PRStreamSource.h>
-
 #include <client/PasswordListener.h>
+#include <client/PasswordSender.h>
 
 /**
  * Callback function invoked when an error occurs with the peer connection.
@@ -116,8 +116,8 @@ typedef struct {
             CERTCertificate *ssl_cert;
             SECKEYPrivateKey *ssl_key;
             sslsocket sock;
-            uint64_t connecting_password;
-            int connecting_sending_sent;
+            uint64_t password;
+            PasswordSender pwsender;
         } connect;
     };
     

+ 1 - 0
flow/CMakeLists.txt

@@ -26,5 +26,6 @@ add_library(flow
     FragmentProtoAssembler.c
     DataProtoKeepaliveSource.c
     PacketProtoFlow.c
+    SinglePacketSender.c
 )
 target_link_libraries(flow system)

+ 105 - 0
flow/SinglePacketSender.c

@@ -0,0 +1,105 @@
+/**
+ * @file SinglePacketSender.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 <flow/SinglePacketSender.h>
+
+static void call_handler (SinglePacketSender *o)
+{
+    #ifndef NDEBUG
+    DEAD_ENTER(o->dead)
+    #endif
+    
+    o->handler(o->user);
+    
+    #ifndef NDEBUG
+    ASSERT(DEAD_KILLED)
+    DEAD_LEAVE(o->dead);
+    #endif
+}
+
+static void output_handler_done (SinglePacketSender *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    // notify user
+    call_handler(o);
+    return;
+}
+
+static void job_handler (SinglePacketSender *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    DEAD_ENTER(o->dead)
+    int res = PacketPassInterface_Sender_Send(o->output, o->packet, o->packet_len);
+    if (DEAD_LEAVE(o->dead)) {
+        return;
+    }
+    
+    ASSERT(res == 0 || res == 1)
+    
+    if (!res) {
+        return;
+    }
+    
+    // notify user
+    call_handler(o);
+    return;
+}
+
+void SinglePacketSender_Init (SinglePacketSender *o, uint8_t *packet, int packet_len, PacketPassInterface *output, SinglePacketSender_handler handler, void *user, BPendingGroup *pg)
+{
+    ASSERT(packet_len >= 0)
+    ASSERT(packet_len <= PacketPassInterface_GetMTU(output))
+    
+    // init arguments
+    o->packet = packet;
+    o->packet_len = packet_len;
+    o->output = output;
+    o->handler = handler;
+    o->user = user;
+    
+    // init dead var
+    DEAD_INIT(o->dead);
+    
+    // init output
+    PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o);
+    
+    // init start job
+    BPending_Init(&o->start_job, pg, (BPending_handler)job_handler, o);
+    BPending_Set(&o->start_job);
+    
+    DebugObject_Init(&o->d_obj);
+}
+
+void SinglePacketSender_Free (SinglePacketSender *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    // free start job
+    BPending_Free(&o->start_job);
+    
+    // free dead var
+    DEAD_KILL(o->dead);
+}

+ 79 - 0
flow/SinglePacketSender.h

@@ -0,0 +1,79 @@
+/**
+ * @file SinglePacketSender.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} source which sends a single packet.
+ */
+
+#ifndef BADVPN_FLOW_SINGLEPACKETSENDER_H
+#define BADVPN_FLOW_SINGLEPACKETSENDER_H
+
+#include <stdint.h>
+
+#include <misc/dead.h>
+#include <system/BPending.h>
+#include <system/DebugObject.h>
+#include <flow/PacketPassInterface.h>
+
+/**
+ * Handler function called after the packet is sent.
+ * The object must be freed from within this handler.
+ * 
+ * @param user as in {@link SinglePacketSender_Init}.
+ */
+typedef void (*SinglePacketSender_handler) (void *user);
+
+/**
+ * A {@link PacketPassInterface} source which sends a single packet.
+ */
+typedef struct {
+    dead_t dead;
+    uint8_t *packet;
+    int packet_len;
+    PacketPassInterface *output;
+    SinglePacketSender_handler handler;
+    void *user;
+    BPending start_job;
+    DebugObject d_obj;
+} SinglePacketSender;
+
+/**
+ * Initializes the object.
+ * 
+ * @param o the object
+ * @param packet packet to be sent. Must be available as long as the object exists.
+ * @param packet_len length of the packet. Must be >=0 and <=(MTU of output).
+ * @param output output interface
+ * @param handler handler to call when the packet is sent
+ * @param user value to pass to handler
+ * @param pg pending group
+ */
+void SinglePacketSender_Init (SinglePacketSender *o, uint8_t *packet, int packet_len, PacketPassInterface *output, SinglePacketSender_handler handler, void *user, BPendingGroup *pg);
+
+/**
+ * Frees the object.
+ * 
+ * @param o the object
+ */
+void SinglePacketSender_Free (SinglePacketSender *o);
+
+#endif