Răsfoiți Sursa

tun2socks: implement SOCKS password authentication

ambrop7 13 ani în urmă
părinte
comite
4192ac388f

+ 257 - 55
socksclient/BSocksClient.c

@@ -28,6 +28,7 @@
  */
 
 #include <misc/byteorder.h>
+#include <misc/balloc.h>
 #include <base/BLog.h>
 
 #include <socksclient/BSocksClient.h>
@@ -37,6 +38,8 @@
 #define STATE_CONNECTING 1
 #define STATE_SENDING_HELLO 2
 #define STATE_SENT_HELLO 3
+#define STATE_SENDING_PASSWORD 10
+#define STATE_SENT_PASSWORD 11
 #define STATE_SENDING_REQUEST 4
 #define STATE_SENT_REQUEST 5
 #define STATE_RECEIVED_REPLY_HEADER 6
@@ -47,12 +50,14 @@ static void init_control_io (BSocksClient *o);
 static void free_control_io (BSocksClient *o);
 static void init_up_io (BSocksClient *o);
 static void free_up_io (BSocksClient *o);
+static int reserve_buffer (BSocksClient *o, bsize_t size);
 static void start_receive (BSocksClient *o, uint8_t *dest, int total);
 static void do_receive (BSocksClient *o);
 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);
+static void auth_finished (BSocksClient *p);
 
 void report_error (BSocksClient *o, int error)
 {
@@ -68,7 +73,7 @@ void init_control_io (BSocksClient *o)
     
     // init sending
     BConnection_SendAsync_Init(&o->con);
-    PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), sizeof(o->control.msg), BReactor_PendingGroup(o->reactor));
+    PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), INT_MAX, 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);
 }
@@ -101,6 +106,24 @@ void free_up_io (BSocksClient *o)
     BConnection_RecvAsync_Free(&o->con);
 }
 
+int reserve_buffer (BSocksClient *o, bsize_t size)
+{
+    if (size.is_overflow) {
+        BLog(BLOG_ERROR, "size overflow");
+        return 0;
+    }
+    
+    char *buffer = BRealloc(o->buffer, size.value);
+    if (!buffer) {
+        BLog(BLOG_ERROR, "BRealloc failed");
+        return 0;
+    }
+    
+    o->buffer = buffer;
+    
+    return 1;
+}
+
 void start_receive (BSocksClient *o, uint8_t *dest, int total)
 {
     ASSERT(total > 0)
@@ -141,17 +164,42 @@ void connector_handler (BSocksClient* o, int is_error)
     // init control I/O
     init_control_io(o);
     
-    // send hello
-    o->control.msg.client_hello.header.ver = hton8(SOCKS_VERSION);
-    o->control.msg.client_hello.header.nmethods = hton8(1);
-    o->control.msg.client_hello.method.method = hton8(SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED);
-    PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)&o->control.msg.client_hello, sizeof(o->control.msg.client_hello));
+    // check number of methods
+    if (o->num_auth_info == 0 || o->num_auth_info > 255) {
+        BLog(BLOG_ERROR, "invalid number of authentication methods");
+        goto fail1;
+    }
+    
+    // allocate buffer for sending hello
+    bsize_t size = bsize_add(
+        bsize_fromsize(sizeof(struct BSocksClient__client_hello)), 
+        bsize_mul(
+            bsize_fromsize(o->num_auth_info),
+            bsize_fromsize(sizeof(struct socks_client_hello_method))
+        )
+    );
+    if (!reserve_buffer(o, size)) {
+        goto fail1;
+    }
+    
+    // build hello
+    struct BSocksClient__client_hello *omsg = (void *)o->buffer;
+    omsg->header.ver = hton8(SOCKS_VERSION);
+    omsg->header.nmethods = hton8(o->num_auth_info);
+    for (size_t i = 0; i < o->num_auth_info; i++) {
+        omsg->methods[i].method = hton8(o->auth_info[i].auth_type);
+    }
+    
+    // send
+    PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value);
     
     // set state
     o->state = STATE_SENDING_HELLO;
     
     return;
     
+fail1:
+    free_control_io(o);
 fail0:
     report_error(o, BSOCKSCLIENT_EVENT_ERROR);
     return;
@@ -188,84 +236,129 @@ void recv_handler_done (BSocksClient *o, int data_len)
         case STATE_SENT_HELLO: {
             BLog(BLOG_DEBUG, "received hello");
             
-            if (ntoh8(o->control.msg.server_hello.ver != SOCKS_VERSION)) {
+            struct socks_server_hello *imsg = (void *)o->buffer;
+            
+            if (ntoh8(imsg->ver) != SOCKS_VERSION) {
                 BLog(BLOG_NOTICE, "wrong version");
-                report_error(o, BSOCKSCLIENT_EVENT_ERROR);
-                return;
+                goto fail;
             }
             
-            if (ntoh8(o->control.msg.server_hello.method != SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED)) {
-                BLog(BLOG_NOTICE, "wrong method");
-                report_error(o, BSOCKSCLIENT_EVENT_ERROR);
-                return;
+            size_t auth_index;
+            for (auth_index = 0; auth_index < o->num_auth_info; auth_index++) {
+                if (o->auth_info[auth_index].auth_type == ntoh8(imsg->method)) {
+                    break;
+                }
             }
             
-            // send request
-            o->control.msg.request.header.ver = hton8(SOCKS_VERSION);
-            o->control.msg.request.header.cmd = hton8(SOCKS_CMD_CONNECT);
-            o->control.msg.request.header.rsv = hton8(0);
-            int len = sizeof(o->control.msg.request.header);
-            switch (o->dest_addr.type) {
-                case BADDR_TYPE_IPV4:
-                    o->control.msg.request.header.atyp = hton8(SOCKS_ATYP_IPV4);
-                    o->control.msg.request.addr.ipv4.addr = o->dest_addr.ipv4.ip;
-                    o->control.msg.request.addr.ipv4.port = o->dest_addr.ipv4.port;
-                    len += sizeof(o->control.msg.request.addr.ipv4);
-                    break;
-                case BADDR_TYPE_IPV6:
-                    o->control.msg.request.header.atyp = hton8(SOCKS_ATYP_IPV6);
-                    memcpy(o->control.msg.request.addr.ipv6.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip));
-                    o->control.msg.request.addr.ipv6.port = o->dest_addr.ipv6.port;
-                    len += sizeof(o->control.msg.request.addr.ipv6);
-                    break;
-                default:
-                    ASSERT(0);
+            if (auth_index == o->num_auth_info) {
+                BLog(BLOG_NOTICE, "server didn't accept any authentication method");
+                goto fail;
             }
-            PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)&o->control.msg.request, len);
             
-            // set state
-            o->state = STATE_SENDING_REQUEST;
+            const struct BSocksClient_auth_info *ai = &o->auth_info[auth_index];
+            
+            switch (ai->auth_type) {
+                case SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED: {
+                    BLog(BLOG_DEBUG, "no authentication");
+                    
+                    auth_finished(o);
+                } break;
+                
+                case SOCKS_METHOD_USERNAME_PASSWORD: {
+                    BLog(BLOG_DEBUG, "password authentication");
+                    
+                    if (ai->password.username_len == 0 || ai->password.username_len > 255 ||
+                        ai->password.password_len == 0 || ai->password.password_len > 255
+                    ) {
+                        BLog(BLOG_NOTICE, "invalid username/password length");
+                        goto fail;
+                    }
+                    
+                    // allocate password packet
+                    bsize_t size = bsize_fromsize(1 + 1 + ai->password.username_len + 1 + ai->password.password_len);
+                    if (!reserve_buffer(o, size)) {
+                        goto fail;
+                    }
+                    
+                    // write password packet
+                    char *ptr = o->buffer;
+                    *ptr++ = 1;
+                    *ptr++ = ai->password.username_len;
+                    memcpy(ptr, ai->password.username, ai->password.username_len);
+                    ptr += ai->password.username_len;
+                    *ptr++ = ai->password.password_len;
+                    memcpy(ptr, ai->password.password, ai->password.password_len);
+                    ptr += ai->password.password_len;
+                    
+                    // start sending
+                    PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value);
+                    
+                    // set state
+                    o->state = STATE_SENDING_PASSWORD;
+                } break;
+                
+                default: ASSERT(0);
+            }
         } break;
         
         case STATE_SENT_REQUEST: {
             BLog(BLOG_DEBUG, "received reply header");
             
-            if (ntoh8(o->control.msg.reply.header.ver) != SOCKS_VERSION) {
+            struct BSocksClient__reply *imsg = (void *)o->buffer;
+            
+            if (ntoh8(imsg->header.ver) != SOCKS_VERSION) {
                 BLog(BLOG_NOTICE, "wrong version");
-                report_error(o, BSOCKSCLIENT_EVENT_ERROR);
-                return;
+                goto fail;
             }
             
-            if (ntoh8(o->control.msg.reply.header.rep) != SOCKS_REP_SUCCEEDED) {
+            if (ntoh8(imsg->header.rep) != SOCKS_REP_SUCCEEDED) {
                 BLog(BLOG_NOTICE, "reply not successful");
-                report_error(o, BSOCKSCLIENT_EVENT_ERROR);
-                return;
+                goto fail;
             }
             
             int addr_len;
-            switch (ntoh8(o->control.msg.reply.header.atyp)) {
+            switch (ntoh8(imsg->header.atyp)) {
                 case SOCKS_ATYP_IPV4:
-                    addr_len = sizeof(o->control.msg.reply.addr.ipv4);
+                    addr_len = sizeof(struct socks_addr_ipv4);
                     break;
                 case SOCKS_ATYP_IPV6:
-                    addr_len = sizeof(o->control.msg.reply.addr.ipv6);
+                    addr_len = sizeof(struct socks_addr_ipv6);
                     break;
                 default:
                     BLog(BLOG_NOTICE, "reply has unknown address type");
-                    report_error(o, BSOCKSCLIENT_EVENT_ERROR);
-                    return;
+                    goto fail;
             }
             
             // receive the rest of the reply
-            start_receive(o, (uint8_t *)&o->control.msg.reply.addr, addr_len);
+            start_receive(o, (uint8_t *)&imsg->addr, addr_len);
             
             // set state
             o->state = STATE_RECEIVED_REPLY_HEADER;
         } break;
         
+        case STATE_SENT_PASSWORD: {
+            BLog(BLOG_DEBUG, "received password reply");
+            
+            if (o->buffer[0] != 1) {
+                BLog(BLOG_NOTICE, "password reply has unknown version");
+                goto fail;
+            }
+            
+            if (o->buffer[1] != 0) {
+                BLog(BLOG_NOTICE, "password reply is negative");
+                goto fail;
+            }
+            
+            auth_finished(o);
+        } break;
+        
         case STATE_RECEIVED_REPLY_HEADER: {
             BLog(BLOG_DEBUG, "received reply rest");
             
+            // free buffer
+            BFree(o->buffer);
+            o->buffer = NULL;
+            
             // free control I/O
             free_control_io(o);
             
@@ -283,18 +376,30 @@ void recv_handler_done (BSocksClient *o, int data_len)
         default:
             ASSERT(0);
     }
+    
+    return;
+    
+fail:
+    report_error(o, BSOCKSCLIENT_EVENT_ERROR);
 }
 
 void send_handler_done (BSocksClient *o)
 {
     DebugObject_Access(&o->d_obj);
+    ASSERT(o->buffer)
     
     switch (o->state) {
         case STATE_SENDING_HELLO: {
             BLog(BLOG_DEBUG, "sent hello");
             
+            // allocate buffer for receiving hello
+            bsize_t size = bsize_fromsize(sizeof(struct socks_server_hello));
+            if (!reserve_buffer(o, size)) {
+                goto fail;
+            }
+            
             // receive hello
-            start_receive(o, (uint8_t *)&o->control.msg.server_hello, sizeof(o->control.msg.server_hello));
+            start_receive(o, (uint8_t *)o->buffer, size.value);
             
             // set state
             o->state = STATE_SENT_HELLO;
@@ -303,29 +408,125 @@ void send_handler_done (BSocksClient *o)
         case STATE_SENDING_REQUEST: {
             BLog(BLOG_DEBUG, "sent request");
             
+            // allocate buffer for receiving reply
+            bsize_t size = bsize_fromsize(sizeof(struct BSocksClient__reply));
+            if (!reserve_buffer(o, size)) {
+                goto fail;
+            }
+            
             // receive reply header
-            start_receive(o, (uint8_t *)&o->control.msg.reply.header, sizeof(o->control.msg.reply.header));
+            start_receive(o, (uint8_t *)o->buffer, sizeof(struct socks_reply_header));
             
             // set state
             o->state = STATE_SENT_REQUEST;
         } break;
         
+        case STATE_SENDING_PASSWORD: {
+            BLog(BLOG_DEBUG, "send password");
+            
+            // allocate buffer for receiving reply
+            bsize_t size = bsize_fromsize(2);
+            if (!reserve_buffer(o, size)) {
+                goto fail;
+            }
+            
+            // receive reply header
+            start_receive(o, (uint8_t *)o->buffer, size.value);
+            
+            // set state
+            o->state = STATE_SENT_PASSWORD;
+        } break;
+        
         default:
             ASSERT(0);
     }
+    
+    return;
+    
+fail:
+    report_error(o, BSOCKSCLIENT_EVENT_ERROR);
 }
 
-int BSocksClient_Init (BSocksClient *o, BAddr server_addr, BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor)
+void auth_finished (BSocksClient *o)
+{
+    // allocate request buffer
+    bsize_t size = bsize_fromsize(sizeof(struct socks_request_header));
+    switch (o->dest_addr.type) {
+        case BADDR_TYPE_IPV4: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4))); break;
+        case BADDR_TYPE_IPV6: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6))); break;
+    }
+    if (!reserve_buffer(o, size)) {
+        report_error(o, BSOCKSCLIENT_EVENT_ERROR);
+        return;
+    }
+    
+    // send request
+    struct BSocksClient__request *omsg = (void *)o->buffer;
+    omsg->header.ver = hton8(SOCKS_VERSION);
+    omsg->header.cmd = hton8(SOCKS_CMD_CONNECT);
+    omsg->header.rsv = hton8(0);
+    switch (o->dest_addr.type) {
+        case BADDR_TYPE_IPV4:
+            omsg->header.atyp = hton8(SOCKS_ATYP_IPV4);
+            omsg->addr.ipv4.addr = o->dest_addr.ipv4.ip;
+            omsg->addr.ipv4.port = o->dest_addr.ipv4.port;
+            break;
+        case BADDR_TYPE_IPV6:
+            omsg->header.atyp = hton8(SOCKS_ATYP_IPV6);
+            memcpy(omsg->addr.ipv6.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip));
+            omsg->addr.ipv6.port = o->dest_addr.ipv6.port;
+            break;
+        default:
+            ASSERT(0);
+    }
+    PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value);
+    
+    // set state
+    o->state = STATE_SENDING_REQUEST;
+}
+
+struct BSocksClient_auth_info BSocksClient_auth_none (void)
+{
+    struct BSocksClient_auth_info info;
+    info.auth_type = SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED;
+    return info;
+}
+
+struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len)
+{
+    struct BSocksClient_auth_info info;
+    info.auth_type = SOCKS_METHOD_USERNAME_PASSWORD;
+    info.password.username = username;
+    info.password.username_len = username_len;
+    info.password.password = password;
+    info.password.password_len = password_len;
+    return info;
+}
+
+int BSocksClient_Init (BSocksClient *o,
+                       BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info,
+                       BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor)
 {
     ASSERT(!BAddr_IsInvalid(&server_addr))
     ASSERT(dest_addr.type == BADDR_TYPE_IPV4 || dest_addr.type == BADDR_TYPE_IPV6)
+#ifndef NDEBUG
+    for (size_t i = 0; i < num_auth_info; i++) {
+        ASSERT(auth_info[i].auth_type == SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED ||
+               auth_info[i].auth_type == SOCKS_METHOD_USERNAME_PASSWORD)
+    }
+#endif
     
     // init arguments
+    o->auth_info = auth_info;
+    o->num_auth_info = num_auth_info;
     o->dest_addr = dest_addr;
     o->handler = handler;
     o->user = user;
     o->reactor = reactor;
     
+    // set no buffer
+    o->buffer = NULL;
+    
     // init connector
     if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) {
         BLog(BLOG_ERROR, "BConnector_Init failed");
@@ -353,10 +554,6 @@ void BSocksClient_Free (BSocksClient *o)
             // 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);
         }
@@ -367,6 +564,11 @@ void BSocksClient_Free (BSocksClient *o)
     
     // free connector
     BConnector_Free(&o->connector);
+    
+    // free buffer
+    if (o->buffer) {
+        BFree(o->buffer);
+    }
 }
 
 StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o)

+ 22 - 8
socksclient/BSocksClient.h

@@ -65,7 +65,7 @@ typedef void (*BSocksClient_handler) (void *user, int event);
 B_START_PACKED
 struct BSocksClient__client_hello {
     struct socks_client_hello_header header;
-    struct socks_client_hello_method method;
+    struct socks_client_hello_method methods[];
 } B_PACKED;
 B_END_PACKED
 
@@ -89,12 +89,27 @@ struct BSocksClient__reply {
 } B_PACKED;
 B_END_PACKED
 
+struct BSocksClient_auth_info {
+    int auth_type;
+    union {
+        struct {
+            const char *username;
+            size_t username_len;
+            const char *password;
+            size_t password_len;
+        } password;
+    };
+};
+
 typedef struct {
+    const struct BSocksClient_auth_info *auth_info;
+    size_t num_auth_info;
     BAddr dest_addr;
     BSocksClient_handler handler;
     void *user;
     BReactor *reactor;
     int state;
+    char *buffer;
     BConnector connector;
     BConnection con;
     union {
@@ -102,12 +117,6 @@ typedef struct {
             PacketPassInterface *send_if;
             PacketStreamSender send_sender;
             StreamRecvInterface *recv_if;
-            union {
-                struct BSocksClient__client_hello client_hello;
-                struct socks_server_hello server_hello;
-                struct BSocksClient__request request;
-                struct BSocksClient__reply reply;
-            } msg;
             uint8_t *recv_dest;
             int recv_len;
             int recv_total;
@@ -117,6 +126,9 @@ typedef struct {
     DebugObject d_obj;
 } BSocksClient;
 
+struct BSocksClient_auth_info BSocksClient_auth_none (void);
+struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len);
+
 /**
  * Initializes the object.
  * The object is initialized in down state. The object must transition to up
@@ -130,7 +142,9 @@ typedef struct {
  * @param reactor reactor we live in
  * @return 1 on success, 0 on failure
  */
-int BSocksClient_Init (BSocksClient *o, BAddr server_addr, BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED;
+int BSocksClient_Init (BSocksClient *o,
+                       BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info,
+                       BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED;
 
 /**
  * Frees the object.

+ 6 - 2
tun2socks/SocksUdpGwClient.c

@@ -63,7 +63,7 @@ static void try_connect (SocksUdpGwClient *o)
     ASSERT(!BTimer_IsRunning(&o->reconnect_timer))
     
     // init SOCKS client
-    if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->remote_udpgw_addr, (BSocksClient_handler)socks_client_handler, o, o->reactor)) {
+    if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, (BSocksClient_handler)socks_client_handler, o, o->reactor)) {
         BLog(BLOG_ERROR, "BSocksClient_Init failed");
         goto fail0;
     }
@@ -159,7 +159,9 @@ static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr
     return;
 }
 
-int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BAddr socks_server_addr, BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user,
+int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time,
+                           BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info,
+                           BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user,
                            SocksUdpGwClient_handler_received handler_received)
 {
     ASSERT(udp_mtu >= 0)
@@ -173,6 +175,8 @@ int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections
     // init arguments
     o->udp_mtu = udp_mtu;
     o->socks_server_addr = socks_server_addr;
+    o->auth_info = auth_info;
+    o->num_auth_info = num_auth_info;
     o->remote_udpgw_addr = remote_udpgw_addr;
     o->reactor = reactor;
     o->user = user;

+ 5 - 1
tun2socks/SocksUdpGwClient.h

@@ -41,6 +41,8 @@ typedef void (*SocksUdpGwClient_handler_received) (void *user, BAddr local_addr,
 typedef struct {
     int udp_mtu;
     BAddr socks_server_addr;
+    const struct BSocksClient_auth_info *auth_info;
+    size_t num_auth_info;
     BAddr remote_udpgw_addr;
     BReactor *reactor;
     void *user;
@@ -53,7 +55,9 @@ typedef struct {
     DebugObject d_obj;
 } SocksUdpGwClient;
 
-int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BAddr socks_server_addr, BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user,
+int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time,
+                           BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info,
+                           BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user,
                            SocksUdpGwClient_handler_received handler_received) WARN_UNUSED;
 void SocksUdpGwClient_Free (SocksUdpGwClient *o);
 void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len);

+ 88 - 2
tun2socks/tun2socks.c

@@ -43,6 +43,7 @@
 #include <misc/byteorder.h>
 #include <misc/balloc.h>
 #include <misc/open_standard_streams.h>
+#include <misc/read_file.h>
 #include <structure/LinkedList1.h>
 #include <base/BLog.h>
 #include <system/BReactor.h>
@@ -98,6 +99,9 @@ struct {
     char *netif_ipaddr;
     char *netif_netmask;
     char *socks_server_addr;
+    char *username;
+    char *password;
+    char *password_file;
     char *udpgw_remote_server_addr;
     int udpgw_max_connections;
     int udpgw_connection_buffer_size;
@@ -135,6 +139,13 @@ BIPAddr netif_netmask;
 // SOCKS server address
 BAddr socks_server_addr;
 
+// allocated password file contents
+uint8_t *password_file_contents;
+
+// SOCKS authentication information
+struct BSocksClient_auth_info socks_auth_info[2];
+size_t socks_num_auth_info;
+
 // remote udpgw server addr, if provided
 BAddr udpgw_remote_server_addr;
 
@@ -266,6 +277,9 @@ int main (int argc, char **argv)
     
     BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
     
+    // clear password contents pointer
+    password_file_contents = NULL;
+    
     // initialize network
     if (!BNetwork_GlobalInit()) {
         BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
@@ -325,7 +339,8 @@ int main (int argc, char **argv)
         
         // init udpgw client
         if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME,
-                                   socks_server_addr, udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received
+                                   socks_server_addr, socks_auth_info, socks_num_auth_info,
+                                   udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received
         )) {
             BLog(BLOG_ERROR, "SocksUdpGwClient_Init failed");
             goto fail4a;
@@ -397,6 +412,7 @@ fail3:
 fail2:
     BReactor_Free(&ss);
 fail1:
+    BFree(password_file_contents);
     BLog(BLOG_NOTICE, "exiting");
     BLog_Free();
 fail0:
@@ -438,6 +454,9 @@ void print_help (const char *name)
         "        --netif-ipaddr <ipaddr>\n"
         "        --netif-netmask <ipnetmask>\n"
         "        --socks-server-addr <addr>\n"
+        "        [--username <username>]\n"
+        "        [--password <password>]\n"
+        "        [--password-file <file>]\n"
         "        [--udpgw-remote-server-addr <addr>]\n"
         "        [--udpgw-max-connections <number>]\n"
         "        [--udpgw-connection-buffer-size <number>]\n"
@@ -472,6 +491,9 @@ int parse_arguments (int argc, char *argv[])
     options.netif_ipaddr = NULL;
     options.netif_netmask = NULL;
     options.socks_server_addr = NULL;
+    options.username = NULL;
+    options.password = NULL;
+    options.password_file = NULL;
     options.udpgw_remote_server_addr = NULL;
     options.udpgw_max_connections = DEFAULT_UDPGW_MAX_CONNECTIONS;
     options.udpgw_connection_buffer_size = DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE;
@@ -584,6 +606,30 @@ int parse_arguments (int argc, char *argv[])
             options.socks_server_addr = argv[i + 1];
             i++;
         }
+        else if (!strcmp(arg, "--username")) {
+            if (1 >= argc - i) {
+                fprintf(stderr, "%s: requires an argument\n", arg);
+                return 0;
+            }
+            options.username = argv[i + 1];
+            i++;
+        }
+        else if (!strcmp(arg, "--password")) {
+            if (1 >= argc - i) {
+                fprintf(stderr, "%s: requires an argument\n", arg);
+                return 0;
+            }
+            options.password = argv[i + 1];
+            i++;
+        }
+        else if (!strcmp(arg, "--password-file")) {
+            if (1 >= argc - i) {
+                fprintf(stderr, "%s: requires an argument\n", arg);
+                return 0;
+            }
+            options.password_file = argv[i + 1];
+            i++;
+        }
         else if (!strcmp(arg, "--udpgw-remote-server-addr")) {
             if (1 >= argc - i) {
                 fprintf(stderr, "%s: requires an argument\n", arg);
@@ -639,11 +685,25 @@ int parse_arguments (int argc, char *argv[])
         return 0;
     }
     
+    if (options.username) {
+        if (!options.password && !options.password_file) {
+            fprintf(stderr, "username given but password not given\n");
+            return 0;
+        }
+        
+        if (options.password && options.password_file) {
+            fprintf(stderr, "--password and --password-file cannot both be given\n");
+            return 0;
+        }
+    }
+    
     return 1;
 }
 
 int process_arguments (void)
 {
+    ASSERT(!password_file_contents)
+    
     // resolve netif ipaddr
     if (!BIPAddr_Resolve(&netif_ipaddr, options.netif_ipaddr, 0)) {
         BLog(BLOG_ERROR, "netif ipaddr: BIPAddr_Resolve failed");
@@ -670,6 +730,31 @@ int process_arguments (void)
         return 0;
     }
     
+    // add none socks authentication method
+    socks_auth_info[0] = BSocksClient_auth_none();
+    socks_num_auth_info = 1;
+    
+    // add password socks authentication method
+    if (options.username) {
+        const char *password;
+        size_t password_len;
+        if (options.password) {
+            password = options.password;
+            password_len = strlen(options.password);
+        } else {
+            if (!read_file(options.password_file, &password_file_contents, &password_len)) {
+                BLog(BLOG_ERROR, "failed to read password file");
+                return 0;
+            }
+            password = (char *)password_file_contents;
+        }
+        
+        socks_auth_info[socks_num_auth_info++] = BSocksClient_auth_password(
+            options.username, strlen(options.username),
+            password, password_len
+        );
+    }
+    
     // resolve remote udpgw server address
     if (options.udpgw_remote_server_addr) {
         if (!BAddr_Parse2(&udpgw_remote_server_addr, options.udpgw_remote_server_addr, NULL, 0, 0)) {
@@ -979,7 +1064,8 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
     #ifdef OVERRIDE_DEST_ADDR
     ASSERT_FORCE(BAddr_Parse2(&addr, OVERRIDE_DEST_ADDR, NULL, 0, 1))
     #endif
-    if (!BSocksClient_Init(&client->socks_client, socks_server_addr, addr, (BSocksClient_handler)client_socks_handler, client, &ss)) {
+    if (!BSocksClient_Init(&client->socks_client, socks_server_addr, socks_auth_info, socks_num_auth_info,
+                           addr, (BSocksClient_handler)client_socks_handler, client, &ss)) {
         BLog(BLOG_ERROR, "listener accept: BSocksClient_Init failed");
         SYNC_BREAK
         free(client);