[SCM] Socket Wrapper Repository - branch master updated
Andreas Schneider
asn at samba.org
Mon Feb 8 18:34:21 UTC 2021
The branch, master has been updated
via 0422a95 test_echo_tcp_sendmsg_recvmsg_fd: add test_tcp_sendmsg_recvmsg_fd_mixed() tests
via b35e3ce test_echo_tcp_sendmsg_recvmsg_fd: add test_tcp_sendmsg_recvmsg_fd_different() tests
via 2d93f75 test_echo_tcp_sendmsg_recvmsg_fd: also test passing the same socket up to 6 times
via 5aa939f test_echo_tcp_sendmsg_recvmsg_fd: split out test_tcp_sendmsg_recvmsg_fd_same()
via cbd5910 test_echo_tcp_sendmsg_recvmsg_fd: split out test_tcp_sendmsg_recvmsg_fd_array()
via b2e366d tests/echo_srv: allow more than once tcp connection at a time
via e896cec swrap: fix fd-passing without 4 padding bytes
via 3c7ef47 swrap: fix invalid read in swrap_sendmsg_unix_scm_rights()
from 13a6aca swrap: fix copy on write leak of ~38M for every fork.
https://git.samba.org/?p=socket_wrapper.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 0422a957c1b8959c5b48d7188a524296dc9b778b
Author: Stefan Metzmacher <metze at samba.org>
Date: Fri Feb 5 16:25:43 2021 +0100
test_echo_tcp_sendmsg_recvmsg_fd: add test_tcp_sendmsg_recvmsg_fd_mixed() tests
Here we mix sockets and other valid file descriptors.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit b35e3ce2e2b78fb7a1ccf6d5e011239c14fe27a9
Author: Stefan Metzmacher <metze at samba.org>
Date: Fri Feb 5 14:40:45 2021 +0100
test_echo_tcp_sendmsg_recvmsg_fd: add test_tcp_sendmsg_recvmsg_fd_different() tests
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit 2d93f75669a432723c90e20c3f9b2e2abfa55545
Author: Stefan Metzmacher <metze at samba.org>
Date: Fri Feb 5 14:20:16 2021 +0100
test_echo_tcp_sendmsg_recvmsg_fd: also test passing the same socket up to 6 times
Note SWRAP_MAX_PASSED_FDS is currently 6.
This test demonstrates that even 64-bit systems required commit:
"swrap: fix fd-passing without 4 padding bytes"
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit 5aa939f2eb3f3ab04b65fde031a0c1f151928121
Author: Stefan Metzmacher <metze at samba.org>
Date: Fri Feb 5 14:10:03 2021 +0100
test_echo_tcp_sendmsg_recvmsg_fd: split out test_tcp_sendmsg_recvmsg_fd_same()
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit cbd5910b1814cad1f1c4e9c9e8515f217edcd4d2
Author: Stefan Metzmacher <metze at samba.org>
Date: Thu Feb 4 17:04:30 2021 +0100
test_echo_tcp_sendmsg_recvmsg_fd: split out test_tcp_sendmsg_recvmsg_fd_array()
This will allow us to test more combinations in order to
get better coverage. For now we just test a single fd.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit b2e366d87bf8b340fe5e0b0186369cda7a94d186
Author: Stefan Metzmacher <metze at samba.org>
Date: Fri Feb 5 15:41:27 2021 +0100
tests/echo_srv: allow more than once tcp connection at a time
We should not wait for the last connection to disconnect,
there would not be any reason to use fork at all.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit e896cecf1fc08d091d09fa29f1cfbfc22dab89ff
Author: Stefan Metzmacher <metze at samba.org>
Date: Thu Feb 4 16:20:13 2021 +0100
swrap: fix fd-passing without 4 padding bytes
We noticed the problem on 32 bit platforms and sending a single
application fd, the hidden pipe-fd doesn't fit into the padding
bytes. This can also happen on 64 bit platforms and an even number
of application fds.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
commit 3c7ef4751527bd8c93d5431d9f1e36c4fe648f3d
Author: Stefan Metzmacher <metze at samba.org>
Date: Fri Feb 5 19:36:26 2021 +0100
swrap: fix invalid read in swrap_sendmsg_unix_scm_rights()
Here the fds_out array is larger than the fds_in array, so we can
only copy the fds_in array using size_fds_in, leaving the last slot
of fds_out untouched, which is filled by fds_out[num_fds_in] = pipefd[0]
later.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Andreas Schneider <asn at samba.org>
-----------------------------------------------------------------------
Summary of changes:
src/socket_wrapper.c | 71 ++++-
tests/echo_srv.c | 2 +-
tests/test_echo_tcp_sendmsg_recvmsg_fd.c | 510 +++++++++++++++++++++++++++----
3 files changed, 518 insertions(+), 65 deletions(-)
Changeset truncated at 500 lines:
diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c
index 43a5892..ece3493 100644
--- a/src/socket_wrapper.c
+++ b/src/socket_wrapper.c
@@ -5450,7 +5450,7 @@ static int swrap_sendmsg_unix_scm_rights(const struct cmsghdr *cmsg,
*new_cmsg = *cmsg;
__fds_out.p = CMSG_DATA(new_cmsg);
fds_out = __fds_out.fds;
- memcpy(fds_out, fds_in, size_fds_out);
+ memcpy(fds_out, fds_in, size_fds_in);
new_cmsg->cmsg_len = cmsg->cmsg_len;
for (i = 0; i < num_fds_in; i++) {
@@ -5962,8 +5962,48 @@ static ssize_t swrap_sendmsg_after_unix(struct msghdr *msg_tmp,
static int swrap_recvmsg_before_unix(struct msghdr *msg_in,
struct msghdr *msg_tmp)
{
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ const size_t cm_extra_space = CMSG_SPACE(sizeof(int));
+ uint8_t *cm_data = NULL;
+ size_t cm_data_space = 0;
+
+ *msg_tmp = *msg_in;
+
+ SWRAP_LOG(SWRAP_LOG_TRACE,
+ "msg_in->msg_controllen=%zu",
+ (size_t)msg_in->msg_controllen);
+
+ /* Nothing to do */
+ if (msg_in->msg_controllen == 0 || msg_in->msg_control == NULL) {
+ return 0;
+ }
+
+ /*
+ * We need to give the kernel a bit more space in order
+ * recv the pipe fd, added by swrap_sendmsg_before_unix()).
+ * swrap_recvmsg_after_unix() will hide it again.
+ */
+ cm_data_space = msg_in->msg_controllen;
+ if (cm_data_space < (INT32_MAX - cm_extra_space)) {
+ cm_data_space += cm_extra_space;
+ }
+ cm_data = calloc(1, cm_data_space);
+ if (cm_data == NULL) {
+ return -1;
+ }
+ memcpy(cm_data, msg_in->msg_control, msg_in->msg_controllen);
+
+ msg_tmp->msg_controllen = cm_data_space;
+ msg_tmp->msg_control = cm_data;
+
+ SWRAP_LOG(SWRAP_LOG_TRACE,
+ "msg_tmp->msg_controllen=%zu",
+ (size_t)msg_tmp->msg_controllen);
+ return 0;
+#else /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
*msg_tmp = *msg_in;
return 0;
+#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */
}
static ssize_t swrap_recvmsg_after_unix(struct msghdr *msg_tmp,
@@ -5976,9 +6016,14 @@ static ssize_t swrap_recvmsg_after_unix(struct msghdr *msg_tmp,
size_t cm_data_space = 0;
int rc = -1;
+ SWRAP_LOG(SWRAP_LOG_TRACE,
+ "msg_tmp->msg_controllen=%zu",
+ (size_t)msg_tmp->msg_controllen);
+
/* Nothing to do */
if (msg_tmp->msg_controllen == 0 || msg_tmp->msg_control == NULL) {
- goto done;
+ *msg_out = *msg_tmp;
+ return ret;
}
for (cmsg = CMSG_FIRSTHDR(msg_tmp);
@@ -6006,15 +6051,27 @@ static ssize_t swrap_recvmsg_after_unix(struct msghdr *msg_tmp,
}
/*
- * msg_tmp->msg_control is still the buffer of the caller.
+ * msg_tmp->msg_control was created by swrap_recvmsg_before_unix()
+ * and msg_out->msg_control is still the buffer of the caller.
*/
- memcpy(msg_tmp->msg_control, cm_data, cm_data_space);
- msg_tmp->msg_controllen = cm_data_space;
+ SAFE_FREE(msg_tmp->msg_control);
+ msg_tmp->msg_control = msg_out->msg_control;
+ msg_tmp->msg_controllen = msg_out->msg_controllen;
+ *msg_out = *msg_tmp;
+
+ cm_data_space = MIN(cm_data_space, msg_out->msg_controllen);
+ memcpy(msg_out->msg_control, cm_data, cm_data_space);
+ msg_out->msg_controllen = cm_data_space;
SAFE_FREE(cm_data);
-done:
-#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+
+ SWRAP_LOG(SWRAP_LOG_TRACE,
+ "msg_out->msg_controllen=%zu",
+ (size_t)msg_out->msg_controllen);
+ return ret;
+#else /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
*msg_out = *msg_tmp;
return ret;
+#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */
}
static ssize_t swrap_sendmsg_before(int fd,
diff --git a/tests/echo_srv.c b/tests/echo_srv.c
index 0aefa9a..fbb6637 100644
--- a/tests/echo_srv.c
+++ b/tests/echo_srv.c
@@ -537,6 +537,7 @@ static void echo_tcp(int sock)
pid_t pid;
while (1) {
+ waitpid(-1, NULL, WNOHANG);
s = accept(sock, &addr.sa.s, &addr.sa_socklen);
if (s == -1 && errno == ECONNABORTED) {
continue;
@@ -575,7 +576,6 @@ static void echo_tcp(int sock)
close(s);
exit(0);
}
- waitpid(-1, NULL, 0);
close(s);
}
diff --git a/tests/test_echo_tcp_sendmsg_recvmsg_fd.c b/tests/test_echo_tcp_sendmsg_recvmsg_fd.c
index 8ae1a60..215d5ff 100644
--- a/tests/test_echo_tcp_sendmsg_recvmsg_fd.c
+++ b/tests/test_echo_tcp_sendmsg_recvmsg_fd.c
@@ -33,33 +33,86 @@ static int teardown(void **state)
return 0;
}
-static void test_tcp_sendmsg_recvmsg_fd(void **state)
+struct test_fd {
+ int fd;
+ struct torture_address sock_addr;
+ struct torture_address peer_addr;
+};
+
+static int test_fill_test_fd(struct test_fd *tfd, int fd)
{
- struct torture_address addr = {
- .sa_socklen = sizeof(struct sockaddr_in),
+ struct torture_address saddr = {
+ .sa_socklen = sizeof(struct sockaddr_storage),
};
- int pass_sock_fd;
- int sv[2];
- int child_fd, parent_fd;
- pid_t pid;
- int rc;
+ struct torture_address paddr = {
+ .sa_socklen = sizeof(struct sockaddr_storage),
+ };
+ int ret;
- (void) state; /* unused */
+ *tfd = (struct test_fd) { .fd = fd, };
- /* create socket file descriptor to be passed */
- pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- assert_int_not_equal(pass_sock_fd, -1);
+ ret = getsockname(fd, &saddr.sa.s, &saddr.sa_socklen);
+ if (ret == -1 && errno == ENOTSOCK) {
+ return 0;
+ }
+ if (ret == -1) {
+ return ret;
+ }
- addr.sa.in.sin_family = AF_INET;
- addr.sa.in.sin_port = htons(torture_server_port());
+ ret = getpeername(fd, &paddr.sa.s, &paddr.sa_socklen);
+ if (ret == -1) {
+ return ret;
+ }
- rc = inet_pton(addr.sa.in.sin_family,
- torture_server_address(AF_INET),
- &addr.sa.in.sin_addr);
- assert_int_equal(rc, 1);
+ tfd->sock_addr = saddr;
+ tfd->peer_addr = paddr;
+ return 0;
+}
- rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
- assert_int_equal(rc, 0);
+static void _assert_torture_address_equal(const struct torture_address *ga,
+ const struct torture_address *ea,
+ const char * const file,
+ const int line)
+{
+ _assert_int_equal(ga->sa_socklen, ea->sa_socklen, file, line);
+ if (ga->sa_socklen == 0) {
+ return;
+ }
+ _assert_memory_equal(&ga->sa, &ea->sa, ga->sa_socklen, file, line);
+}
+
+#define assert_test_fd_equal(gfd, efd) \
+ _assert_test_fd_equal(gfd, efd, __FILE__, __LINE__)
+
+static void _assert_test_fd_equal(const struct test_fd *gfd,
+ const struct test_fd *efd,
+ const char * const file,
+ const int line)
+{
+ if (efd->fd == -1) {
+ _assert_int_equal(gfd->fd, -1, file, line);
+ return;
+ }
+
+ _assert_int_not_equal(gfd->fd, -1, file, line);
+
+ _assert_torture_address_equal(&gfd->sock_addr, &efd->sock_addr, file, line);
+ _assert_torture_address_equal(&gfd->peer_addr, &efd->peer_addr, file, line);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_array(const int *fds, size_t num_fds)
+{
+ struct test_fd tfds[num_fds];
+ size_t idx;
+ int sv[2];
+ int child_fd, parent_fd;
+ pid_t pid;
+ int rc;
+
+ for (idx = 0; idx < num_fds; idx++) {
+ rc = test_fill_test_fd(&tfds[idx], fds[idx]);
+ assert_int_equal(rc, 0);
+ }
/* create unix domain socket stream */
rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
@@ -73,17 +126,11 @@ static void test_tcp_sendmsg_recvmsg_fd(void **state)
if (pid == 0) {
/* Child */
- struct torture_address peer_addr = {
- .sa_socklen = sizeof(struct sockaddr_in),
- };
struct msghdr child_msg;
- char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ int recv_fd_array[num_fds];
+ char cmsgbuf[CMSG_SPACE(sizeof(int)*num_fds)];
struct cmsghdr *cmsg;
- int rcv_sock_fd, port;
ssize_t ret;
- char send_buf[64] = {0};
- char recv_buf[64] = {0};
- char ipstr[INET_ADDRSTRLEN];
char byte = { 0, };
struct iovec iov;
@@ -104,45 +151,70 @@ static void test_tcp_sendmsg_recvmsg_fd(void **state)
assert_non_null(cmsg);
assert_int_equal(cmsg->cmsg_type, SCM_RIGHTS);
- memcpy(&rcv_sock_fd, CMSG_DATA(cmsg), sizeof(rcv_sock_fd));
- assert_int_not_equal(rcv_sock_fd, -1);
-
- /* extract peer info from received socket fd */
- ret = getpeername(rcv_sock_fd, &peer_addr.sa.s, &peer_addr.sa_socklen);
- assert_int_not_equal(ret, -1);
-
- port = ntohs(peer_addr.sa.in.sin_port);
- inet_ntop(AF_INET, &peer_addr.sa.in.sin_addr, ipstr, sizeof(ipstr));
-
- /* check whether it is the same socket previously connected */
- assert_string_equal(ipstr, torture_server_address(AF_INET));
- assert_int_equal(port, torture_server_port());
+ memcpy(recv_fd_array, CMSG_DATA(cmsg), sizeof(int)*num_fds);
+ for (idx = 0; idx < num_fds; idx++) {
+ assert_int_not_equal(recv_fd_array[idx], -1);
+ }
- snprintf(send_buf, sizeof(send_buf), "packet");
+ for (idx = 0; idx < num_fds; idx++) {
+ struct test_fd recv_tfd = { .fd = -1, };
- ret = write(rcv_sock_fd,
- send_buf,
- sizeof(send_buf));
- assert_int_not_equal(ret, -1);
+ ret = test_fill_test_fd(&recv_tfd, recv_fd_array[idx]);
+ assert_int_equal(ret, 0);
- ret = read(rcv_sock_fd,
- recv_buf,
- sizeof(recv_buf));
- assert_int_not_equal(ret, -1);
+ assert_test_fd_equal(&recv_tfd, &tfds[idx]);
+ }
- assert_memory_equal(send_buf, recv_buf, sizeof(send_buf));
+ for (idx = 0; idx < num_fds; idx++) {
+ int recv_fd = recv_fd_array[idx];
+ char send_buf[64] = {0,};
+ char recv_buf[64] = {0,};
+
+ if (tfds[idx].sock_addr.sa_socklen == 0) {
+ /*
+ * skip fds not belonging to
+ * a socket.
+ */
+ continue;
+ }
+
+ if (tfds[idx].sock_addr.sa.s.sa_family == AF_UNIX) {
+ /*
+ * skip fds not belonging to
+ * a socket.
+ */
+ continue;
+ }
+
+ snprintf(send_buf, sizeof(send_buf), "packet");
+
+ ret = write(recv_fd,
+ send_buf,
+ sizeof(send_buf));
+ assert_int_not_equal(ret, -1);
+
+ ret = read(recv_fd,
+ recv_buf,
+ sizeof(recv_buf));
+ assert_int_not_equal(ret, -1);
+
+ assert_memory_equal(send_buf, recv_buf, sizeof(send_buf));
+ }
exit(0);
} else {
/* Parent */
struct msghdr parent_msg;
struct cmsghdr *cmsg;
- char cmsgbuf[CMSG_SPACE(sizeof(pass_sock_fd))];
+ char cmsgbuf[CMSG_SPACE(sizeof(int)*num_fds)];
+ int pass_fd_array[num_fds];
char byte = '!';
struct iovec iov;
int cs;
- (void) state; /* unused */
+ for (idx = 0; idx < num_fds; idx++) {
+ pass_fd_array[idx] = tfds[idx].fd;
+ }
iov.iov_base = &byte;
iov.iov_len = 1;
@@ -156,10 +228,10 @@ static void test_tcp_sendmsg_recvmsg_fd(void **state)
cmsg = CMSG_FIRSTHDR(&parent_msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(pass_sock_fd));
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int)*num_fds);
/* place previously connected socket fd as ancillary data */
- memcpy(CMSG_DATA(cmsg), &pass_sock_fd, sizeof(pass_sock_fd));
+ memcpy(CMSG_DATA(cmsg), pass_fd_array, sizeof(int)*num_fds);
parent_msg.msg_controllen = cmsg->cmsg_len;
rc = sendmsg(parent_fd, &parent_msg, 0);
@@ -173,13 +245,337 @@ static void test_tcp_sendmsg_recvmsg_fd(void **state)
}
}
+static void test_tcp_sendmsg_recvmsg_fd_same(size_t num_fds)
+{
+ struct torture_address addr = {
+ .sa_socklen = sizeof(struct sockaddr_in),
+ };
+ int pass_sock_fd;
+ int fd_array[num_fds];
+ size_t idx;
+ int rc;
+
+ /* create socket file descriptor to be passed */
+ pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert_int_not_equal(pass_sock_fd, -1);
+
+ addr.sa.in.sin_family = AF_INET;
+ addr.sa.in.sin_port = htons(torture_server_port());
+
+ rc = inet_pton(addr.sa.in.sin_family,
+ torture_server_address(AF_INET),
+ &addr.sa.in.sin_addr);
+ assert_int_equal(rc, 1);
+
+ rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
+ assert_int_equal(rc, 0);
+
+ for (idx = 0; idx < num_fds; idx++) {
+ fd_array[idx] = pass_sock_fd;
+ }
+
+ test_tcp_sendmsg_recvmsg_fd_array(fd_array, num_fds);
+
+ close(pass_sock_fd);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_1(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_same(1);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_2s(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_same(2);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_3s(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_same(3);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_4s(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_same(4);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_5s(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_same(5);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_6s(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_same(6);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_different(size_t num_fds)
+{
+ int fd_array[num_fds];
+ size_t idx;
+
+ for (idx = 0; idx < num_fds; idx++) {
+ struct torture_address addr = {
+ .sa_socklen = sizeof(struct sockaddr_in),
+ };
+ int pass_sock_fd;
+ int rc;
+
+ /* create socket file descriptor to be passed */
+ pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert_int_not_equal(pass_sock_fd, -1);
+
+ addr.sa.in.sin_family = AF_INET;
+ addr.sa.in.sin_port = htons(torture_server_port());
+
+ rc = inet_pton(addr.sa.in.sin_family,
+ torture_server_address(AF_INET),
+ &addr.sa.in.sin_addr);
+ assert_int_equal(rc, 1);
+
+ rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
+ assert_int_equal(rc, 0);
+
+ fd_array[idx] = pass_sock_fd;
+ }
+
+ test_tcp_sendmsg_recvmsg_fd_array(fd_array, num_fds);
+
+ for (idx = 0; idx < num_fds; idx++) {
+ close(fd_array[idx]);
+ }
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_2d(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_different(2);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_3d(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_different(3);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_4d(void **state)
+{
+ (void) state; /* unused */
+ test_tcp_sendmsg_recvmsg_fd_different(4);
+}
+
--
Socket Wrapper Repository
More information about the samba-cvs
mailing list