Code

unixsock_test: Fix a potential uninitialized memory access in mock_write.
[sysdb.git] / t / unit / utils / unixsock_test.c
1 /*
2  * SysDB - t/unit/utils/unixsock_test.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 /* required for fopencookie support */
33 #ifndef _GNU_SOURCE
34 #       define _GNU_SOURCE
35 #endif
37 #include "utils/unixsock.h"
38 #include "testutils.h"
40 #include <check.h>
42 #include <dlfcn.h>
44 #include <errno.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <sys/un.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
54 #include <unistd.h>
56 /*
57  * I/O "hook" functions
58  */
60 typedef struct {
61         int fd;
62         size_t pos;
63 } io_cookie_t;
65 static struct {
66         const char *data;
67         size_t len;
68 } golden_data[] = {
69         { "a", 1 },
70         { "abc", 3 },
71         { "12345", 5 },
72         { "", 0 },
73 };
75 static char *last_write = NULL;
77 static unsigned long long mock_read_called = 0;
78 static ssize_t
79 mock_read(void *cookie, char *buf, size_t size)
80 {
81         io_cookie_t *c = cookie;
82         ssize_t ret;
84         ++mock_read_called;
86         if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
87                 return 0;
89         ret = snprintf(buf, size, "%s\n", golden_data[c->pos].data);
90         ++c->pos;
91         return ret;
92 } /* mock_read */
94 static unsigned long long mock_write_called = 0;
95 static ssize_t
96 mock_write(void *cookie, const char *buf, size_t size)
97 {
98         io_cookie_t *c = cookie;
100         ++mock_write_called;
102         if (c->pos >= SDB_STATIC_ARRAY_LEN(golden_data))
103                 return 0;
105         if (last_write)
106                 free(last_write);
107         last_write = strndup(buf, size);
108         ++c->pos;
109         return (ssize_t)size;
110 } /* mock_write */
112 /* unsupported: int seek(void *cookie, off64_t *offset, int whence) */
114 static int
115 mock_close(void *cookie)
117         io_cookie_t *c = cookie;
119         if (! c)
120                 return EBADF;
122         close(c->fd);
123         free(c);
124         return 0;
125 } /* mock_close */
127 static cookie_io_functions_t mock_io = {
128         /* read = */  mock_read,
129         /* write = */ mock_write,
130         /* seek = */  NULL,
131         /* close = */ mock_close,
132 };
134 /*
135  * mocked functions
136  */
138 static int myfd = -1;
140 static void *
141 dlopen_libc(void)
143         static void *libc = NULL;
145         if (libc)
146                 return libc;
148         libc = dlopen("libc.so.6", RTLD_LAZY);
149         if (! libc)
150                 fail("INTERNAL ERROR: failed to load libc");
151         return libc;
152 } /* dlopen_libc */
154 int
155 socket(int domain, int __attribute__((unused)) type,
156                 int __attribute__((unused)) protocol)
158         char tmp_file[] = "unixsock_test_socket.XXXXXX";
159         int tmp_fd;
161         /* we only want to mock UNIX sockets; return an error else */
162         if (domain != AF_UNIX) {
163                 errno = EAFNOSUPPORT;
164                 return -1;
165         }
167         /* create an 'anonymous' file to have a valid file-descriptor
168          * which can be close()ed by the caller */
169         tmp_fd = mkstemp(tmp_file);
170         if (tmp_fd < 0)
171                 return -1;
173         unlink(tmp_file);
174         myfd = tmp_fd;
175         return tmp_fd;
176 } /* socket */
178 int
179 connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
181         if (sockfd < 0) {
182                 errno = EBADF;
183                 return -1;
184         }
186         /* we only want to mock UNIX sockets; return an error else */
187         if ((addrlen != sizeof(struct sockaddr_un)) || (! addr)
188                         || (! ((const struct sockaddr_un *)addr)->sun_path)) {
189                 errno = EAFNOSUPPORT;
190                 return -1;
191         }
192         return 0;
193 } /* connect */
195 FILE *
196 fdopen(int fd, const char *mode)
198         io_cookie_t *cookie;
200         if (fd < 0) {
201                 errno = EBADF;
202                 return NULL;
203         }
205         /* check also uses fdopen; in that case we need
206          * to use the original implementation */
207         if (fd != myfd) {
208                 void *libc = dlopen_libc();
209                 FILE *(*orig_fdopen)(int, const char *);
211                 orig_fdopen = (FILE *(*)(int, const char *))dlsym(libc, "fdopen");
213                 if (! orig_fdopen)
214                         fail("INTERNAL ERROR: failed to load fdopen() from libc");
216                 return orig_fdopen(fd, mode);
217         }
219         cookie = calloc(sizeof(*cookie), 1);
220         if (! cookie)
221                 return NULL;
223         cookie->fd = fd;
224         cookie->pos = 0;
225         return fopencookie(cookie, mode, mock_io);
226 } /* fdopen */
228 /*
229  * private variables
230  */
232 static sdb_unixsock_client_t *client;
234 static void
235 setup(void)
237         client = sdb_unixsock_client_create("unixsock_test_path");
238         fail_unless(client != NULL,
239                         "sdb_unixsock_client_create() = NULL; "
240                         "expected unixsock client object");
241 } /* setup */
243 static void
244 teardown(void)
246         sdb_unixsock_client_destroy(client);
247         client = NULL;
248 } /* teardown */
250 static void
251 conn(void)
253         int check;
255         check = sdb_unixsock_client_connect(client);
256         fail_unless(check == 0,
257                         "sdb_unixsock_client_connect() = %i; expected: 0", check);
258 } /* conn */
260 /*
261  * tests
262  */
264 START_TEST(test_sdb_unixsock_client_create)
266         sdb_unixsock_client_t *c;
267         const char *check;
269         c = sdb_unixsock_client_create(NULL);
270         fail_unless(c == NULL,
271                         "sdb_unixsock_client_create() = %p; expected: NULL", c);
273         c = sdb_unixsock_client_create("unixsock_test_path");
274         fail_unless(c != NULL,
275                         "sdb_unixsock_client_create() = NULL; "
276                         "expected unixsock client object");
278         check = sdb_unixsock_client_path(c);
279         fail_unless(check != NULL,
280                         "sdb_unixsock_client_create() did not store path name");
281         fail_unless(!strcmp(check, "unixsock_test_path"),
282                         "sdb_unixsock_client_create() did not store correct path name; "
283                         "got: '%s'; expected: 'unixsock_test_path'", check);
284         sdb_unixsock_client_destroy(c);
286 END_TEST
288 START_TEST(test_sdb_unixsock_client_connect)
290         int check;
292         check = sdb_unixsock_client_connect(client);
293         fail_unless(check == 0,
294                         "sdb_unixsock_client_connect() = %i; expected: 0", check);
296 END_TEST
298 START_TEST(test_sdb_unixsock_client_send)
300         size_t i;
302         conn();
304         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
305                 int check;
307                 mock_write_called = 0;
308                 check = sdb_unixsock_client_send(client, golden_data[i].data);
309                 /* client_send appends \r\n */
310                 fail_unless((size_t)check == golden_data[i].len + 2,
311                                 "sdb_unixsock_client_send() = %i; expected: %zu",
312                                 check, golden_data[i].len + 2);
313                 fail_unless(mock_write_called == 1,
314                                 "sdb_unixsock_client_send() called mock_write %llu times; "
315                                 "expected: 1", mock_write_called);
316                 fail_unless(last_write != NULL,
317                                 "INTERNAL ERROR: mock_write did not record last write");
318                 fail_unless((last_write[check - 1] == '\n')
319                                         && (last_write[check - 2] == '\r'),
320                                 "sdb_unixsock_client_send() did not append \\r\\n "
321                                 "before sending; got: '%s'", last_write);
322                 fail_unless(!strncmp(last_write, golden_data[i].data,
323                                         (size_t)check - 2),
324                                 "sdb_unixsock_client_send() sent unexpected string '%s'; "
325                                 "expected: '%s'", last_write, golden_data[i].data);
326                 free(last_write);
327                 last_write = NULL;
328         }
330 END_TEST
332 START_TEST(test_sdb_unixsock_client_recv)
334         size_t i;
336         conn();
338         for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
339                 char *check;
340                 char buf[64];
342                 mock_read_called = 0;
343                 check = sdb_unixsock_client_recv(client, buf, sizeof(buf));
344                 fail_unless(check != NULL,
345                                 "sdb_unixsock_client_recv() = NULL; expected: a string");
346                 fail_unless(check == buf,
347                                 "sdb_unixsock_client_recv() did not return a pointer "
348                                 "to the user-provided buffer");
349                 fail_unless(mock_read_called == 1,
350                                 "sdb_unixsock_client_recv() called mock_read %llu times; "
351                                 "expected: 1", mock_read_called);
352                 fail_unless(strlen(check) == golden_data[i].len,
353                                 "sdb_unixsock_client_recv() returned string of length "
354                                 "%zu ('%s'); expected: %zu",
355                                 strlen(check), check, golden_data[i].len);
356                 fail_unless(check[golden_data[i].len] != '\n',
357                                 "sdb_unixsock_client_recv() did not strip newline");
358                 fail_unless(!strcmp(check, golden_data[i].data),
359                                 "sdb_unixsock_client_recv() = '%s'; expected: '%s'",
360                                 check, golden_data[i].data);
361         }
363 END_TEST
365 TEST_MAIN("utils::unixsock")
367         TCase *tc = tcase_create("core");
368         tcase_add_checked_fixture(tc, setup, teardown);
369         tcase_add_test(tc, test_sdb_unixsock_client_create);
370         tcase_add_test(tc, test_sdb_unixsock_client_connect);
371         tcase_add_test(tc, test_sdb_unixsock_client_send);
372         tcase_add_test(tc, test_sdb_unixsock_client_recv);
373         ADD_TCASE(tc);
375 TEST_MAIN_END
377 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */