Code

f9929dacd3c63eedb3919400d210e77445f55771
[sysdb.git] / src / client / sock.c
1 /*
2  * SysDB - src/client/sock.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 #include "client/sock.h"
33 #include "utils/error.h"
34 #include "utils/strbuf.h"
35 #include "utils/proto.h"
36 #include "utils/os.h"
38 #include <arpa/inet.h>
40 #include <assert.h>
41 #include <errno.h>
42 #include <limits.h>
44 #include <stdlib.h>
46 #include <string.h>
47 #include <strings.h>
49 #include <unistd.h>
51 #include <sys/socket.h>
52 #include <sys/un.h>
54 #include <netdb.h>
56 /*
57  * private data types
58  */
60 struct sdb_client {
61         char *address;
62         int   fd;
63         bool  eof;
65         ssize_t (*read)(sdb_client_t *, sdb_strbuf_t *, size_t);
66         ssize_t (*write)(sdb_client_t *, const void *, size_t);
67 };
69 /*
70  * private helper functions
71  */
73 static ssize_t
74 client_read(sdb_client_t *client, sdb_strbuf_t *buf, size_t n)
75 {
76         return sdb_strbuf_read(buf, client->fd, n);
77 } /* client_read */
79 static ssize_t
80 client_write(sdb_client_t *client, const void *buf, size_t n)
81 {
82         return sdb_write(client->fd, n, buf);
83 } /* client_write */
85 static int
86 connect_unixsock(sdb_client_t *client, const char *address)
87 {
88         struct sockaddr_un sa;
90         client->fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
91         if (client->fd < 0) {
92                 char errbuf[1024];
93                 sdb_log(SDB_LOG_ERR, "Failed to open socket: %s",
94                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
95                 return -1;
96         }
98         sa.sun_family = AF_UNIX;
99         strncpy(sa.sun_path, address, sizeof(sa.sun_path));
100         sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
102         if (connect(client->fd, (struct sockaddr *)&sa, sizeof(sa))) {
103                 char errbuf[1024];
104                 sdb_client_close(client);
105                 sdb_log(SDB_LOG_ERR, "Failed to connect to '%s': %s",
106                                 sa.sun_path, sdb_strerror(errno, errbuf, sizeof(errbuf)));
107                 return -1;
108         }
109         return client->fd;
110 } /* connect_unixsock */
112 static int
113 connect_tcp(sdb_client_t *client, const char *address)
115         struct addrinfo *ai, *ai_list = NULL;
116         int status;
118         if ((status = sdb_resolve(SDB_NET_TCP, address, &ai_list))) {
119                 sdb_log(SDB_LOG_ERR, "Failed to resolve '%s': %s",
120                                 address, gai_strerror(status));
121                 return -1;
122         }
124         for (ai = ai_list; ai != NULL; ai = ai->ai_next) {
125                 client->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
126                 if (client->fd < 0) {
127                         char errbuf[1024];
128                         sdb_log(SDB_LOG_ERR, "Failed to open socket: %s",
129                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
130                         continue;
131                 }
133                 if (connect(client->fd, ai->ai_addr, ai->ai_addrlen)) {
134                         char host[1024], port[32], errbuf[1024];
135                         sdb_client_close(client);
136                         getnameinfo(ai->ai_addr, ai->ai_addrlen, host, sizeof(host),
137                                         port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
138                         sdb_log(SDB_LOG_ERR, "Failed to connect to '%s:%s': %s",
139                                         host, port, sdb_strerror(errno, errbuf, sizeof(errbuf)));
140                         continue;
141                 }
142                 break;
143         }
144         freeaddrinfo(ai_list);
145         return client->fd;
146 } /* connect_tcp */
148 /*
149  * public API
150  */
152 sdb_client_t *
153 sdb_client_create(const char *address)
155         sdb_client_t *client;
157         if (! address)
158                 return NULL;
160         client = malloc(sizeof(*client));
161         if (! client) {
162                 sdb_log(SDB_LOG_ERR, "Out of memory");
163                 return NULL;
164         }
165         memset(client, 0, sizeof(*client));
166         client->fd = -1;
167         client->eof = 1;
169         client->read = client_read;
170         client->write = client_write;
172         client->address = strdup(address);
173         if (! client->address) {
174                 sdb_client_destroy(client);
175                 sdb_log(SDB_LOG_ERR, "Out of memory");
176                 return NULL;
177         }
179         return client;
180 } /* sdb_client_create */
182 void
183 sdb_client_destroy(sdb_client_t *client)
185         if (! client)
186                 return;
188         sdb_client_close(client);
190         if (client->address)
191                 free(client->address);
192         client->address = NULL;
194         free(client);
195 } /* sdb_client_destroy */
197 int
198 sdb_client_connect(sdb_client_t *client, const char *username)
200         sdb_strbuf_t *buf;
201         ssize_t status;
202         uint32_t rstatus;
204         if ((! client) || (! client->address))
205                 return -1;
207         if (client->fd >= 0)
208                 return -1;
210         if (*client->address == '/')
211                 connect_unixsock(client, client->address);
212         else if (!strncasecmp(client->address, "unix:", strlen("unix:")))
213                 connect_unixsock(client, client->address + strlen("unix:"));
214         else if (!strncasecmp(client->address, "tcp:", strlen("tcp:")))
215                 connect_tcp(client, client->address + strlen("tcp:"));
216         else
217                 connect_tcp(client, client->address);
219         if (client->fd < 0)
220                 return -1;
221         client->eof = 0;
223         /* XXX */
224         if (! username)
225                 username = "";
227         buf = sdb_strbuf_create(64);
228         rstatus = 0;
229         status = sdb_client_rpc(client, SDB_CONNECTION_STARTUP,
230                         (uint32_t)strlen(username), username, &rstatus, buf);
231         if ((status >= 0) && (rstatus == SDB_CONNECTION_OK)) {
232                 sdb_strbuf_destroy(buf);
233                 return 0;
234         }
236         if (status < 0) {
237                 sdb_log(SDB_LOG_ERR, "%s", sdb_strbuf_string(buf));
238                 sdb_client_close(client);
239                 sdb_strbuf_destroy(buf);
240                 return (int)status;
241         }
242         if (client->eof)
243                 sdb_log(SDB_LOG_ERR, "Encountered end-of-file while waiting "
244                                 "for server response");
246         if (rstatus == SDB_CONNECTION_ERROR) {
247                 sdb_log(SDB_LOG_ERR, "Access denied for user '%s': %s",
248                                 username, sdb_strbuf_string(buf));
249                 status = -((int)rstatus);
250         }
251         else if (rstatus != SDB_CONNECTION_OK) {
252                 sdb_log(SDB_LOG_ERR, "Received unsupported authentication request "
253                                 "(status %d) during startup", (int)rstatus);
254                 status = -((int)rstatus);
255         }
257         sdb_client_close(client);
258         sdb_strbuf_destroy(buf);
259         return (int)status;
260 } /* sdb_client_connect */
262 int
263 sdb_client_sockfd(sdb_client_t *client)
265         if (! client)
266                 return -1;
267         return client->fd;
268 } /* sdb_client_sockfd */
270 int
271 sdb_client_shutdown(sdb_client_t *client, int how)
273         if (! client) {
274                 errno = ENOTSOCK;
275                 return -1;
276         }
278         if (client->fd < 0) {
279                 errno = EBADF;
280                 return -1;
281         }
283         return shutdown(client->fd, how);
284 } /* sdb_client_shutdown */
286 void
287 sdb_client_close(sdb_client_t *client)
289         if (! client)
290                 return;
292         close(client->fd);
293         client->fd = -1;
294         client->eof = 1;
295 } /* sdb_client_close */
297 ssize_t
298 sdb_client_rpc(sdb_client_t *client,
299                 uint32_t cmd, uint32_t msg_len, const char *msg,
300                 uint32_t *code, sdb_strbuf_t *buf)
302         uint32_t rcode = 0;
303         ssize_t status;
305         if (! buf)
306                 return -1;
308         if (sdb_client_send(client, cmd, msg_len, msg) < 0) {
309                 char errbuf[1024];
310                 sdb_strbuf_sprintf(buf, "Failed to send %s message to server: %s",
311                                 SDB_CONN_MSGTYPE_TO_STRING(cmd),
312                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
313                 if (code)
314                         *code = SDB_CONNECTION_ERROR;
315                 return -1;
316         }
318         while (42) {
319                 size_t offset = sdb_strbuf_len(buf);
321                 status = sdb_client_recv(client, &rcode, buf);
322                 if (status < 0) {
323                         char errbuf[1024];
324                         sdb_strbuf_sprintf(buf, "Failed to receive server response: %s",
325                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
326                         if (code)
327                                 *code = SDB_CONNECTION_ERROR;
328                         return status;
329                 }
331                 if (rcode == SDB_CONNECTION_LOG) {
332                         uint32_t prio = 0;
333                         if (sdb_proto_unmarshal_int32(SDB_STRBUF_STR(buf), &prio) < 0) {
334                                 sdb_log(SDB_LOG_WARNING, "Received a LOG message "
335                                                 "with invalid or missing priority");
336                                 prio = (uint32_t)SDB_LOG_ERR;
337                         }
338                         sdb_log((int)prio, "%s", sdb_strbuf_string(buf) + offset);
339                         sdb_strbuf_skip(buf, offset, sdb_strbuf_len(buf) - offset);
340                         continue;
341                 }
342                 break;
343         }
345         if (code)
346                 *code = rcode;
347         return status;
348 } /* sdb_client_rpc */
350 ssize_t
351 sdb_client_send(sdb_client_t *client,
352                 uint32_t cmd, uint32_t msg_len, const char *msg)
354         char buf[2 * sizeof(uint32_t) + msg_len];
356         if ((! client) || (! client->fd))
357                 return -1;
358         if (sdb_proto_marshal(buf, sizeof(buf), cmd, msg_len, msg) < 0)
359                 return -1;
361         return client->write(client, buf, sizeof(buf));
362 } /* sdb_client_send */
364 ssize_t
365 sdb_client_recv(sdb_client_t *client,
366                 uint32_t *code, sdb_strbuf_t *buf)
368         uint32_t rstatus = UINT32_MAX;
369         uint32_t rlen = UINT32_MAX;
371         size_t total = 0;
372         size_t req = 2 * sizeof(uint32_t);
374         size_t data_offset = sdb_strbuf_len(buf);
376         if (code)
377                 *code = UINT32_MAX;
379         if ((! client) || (! client->fd) || (! buf)) {
380                 errno = EBADF;
381                 return -1;
382         }
384         while (42) {
385                 ssize_t status;
387                 errno = 0;
388                 status = client->read(client, buf, req);
389                 if (status < 0) {
390                         if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
391                                 continue;
392                         return status;
393                 }
394                 else if (! status) {
395                         client->eof = 1;
396                         break;
397                 }
399                 total += (size_t)status;
401                 if (total != req)
402                         continue;
404                 if (rstatus == UINT32_MAX) {
405                         const char *str = sdb_strbuf_string(buf) + data_offset;
406                         size_t len = sdb_strbuf_len(buf) - data_offset;
407                         ssize_t n;
409                         /* retrieve status and data len */
410                         assert(len >= 2 * sizeof(uint32_t));
411                         n = sdb_proto_unmarshal_int32(str, len, &rstatus);
412                         str += n; len -= (size_t)n;
413                         sdb_proto_unmarshal_int32(str, len, &rlen);
415                         if (! rlen)
416                                 break;
418                         req = (size_t)rlen;
419                         total = 0;
420                 }
421                 else /* finished reading data */
422                         break;
423         }
425         if (total != req) {
426                 /* unexpected EOF; clear partially read data */
427                 sdb_strbuf_skip(buf, data_offset, sdb_strbuf_len(buf));
428                 return 0;
429         }
431         if (rstatus != UINT32_MAX)
432                 /* remove status,len */
433                 sdb_strbuf_skip(buf, data_offset, 2 * sizeof(rstatus));
435         if (code)
436                 *code = rstatus;
438         return (ssize_t)total;
439 } /* sdb_client_recv */
441 bool
442 sdb_client_eof(sdb_client_t *client)
444         if ((! client) || (client->fd < 0))
445                 return 1;
446         return client->eof;
447 } /* sdb_client_eof */
449 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */