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 /*
55 * private data types
56 */
58 struct sdb_client {
59 char *address;
60 int fd;
61 bool eof;
62 };
64 /*
65 * private helper functions
66 */
68 static int
69 connect_unixsock(sdb_client_t *client, const char *address)
70 {
71 struct sockaddr_un sa;
73 client->fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
74 if (client->fd < 0) {
75 char errbuf[1024];
76 sdb_log(SDB_LOG_ERR, "Failed to open socket: %s",
77 sdb_strerror(errno, errbuf, sizeof(errbuf)));
78 return -1;
79 }
81 sa.sun_family = AF_UNIX;
82 strncpy(sa.sun_path, address, sizeof(sa.sun_path));
83 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
85 if (connect(client->fd, (struct sockaddr *)&sa, sizeof(sa))) {
86 char errbuf[1024];
87 sdb_client_close(client);
88 sdb_log(SDB_LOG_ERR, "Failed to connect to '%s': %s",
89 sa.sun_path, sdb_strerror(errno, errbuf, sizeof(errbuf)));
90 return -1;
91 }
92 return client->fd;
93 } /* connect_unixsock */
95 /*
96 * public API
97 */
99 sdb_client_t *
100 sdb_client_create(const char *address)
101 {
102 sdb_client_t *client;
104 if (! address)
105 return NULL;
107 client = malloc(sizeof(*client));
108 if (! client) {
109 sdb_log(SDB_LOG_ERR, "Out of memory");
110 return NULL;
111 }
112 memset(client, 0, sizeof(*client));
113 client->fd = -1;
114 client->eof = 1;
116 client->address = strdup(address);
117 if (! client->address) {
118 sdb_client_destroy(client);
119 sdb_log(SDB_LOG_ERR, "Out of memory");
120 return NULL;
121 }
123 return client;
124 } /* sdb_client_create */
126 void
127 sdb_client_destroy(sdb_client_t *client)
128 {
129 if (! client)
130 return;
132 sdb_client_close(client);
134 if (client->address)
135 free(client->address);
136 client->address = NULL;
138 free(client);
139 } /* sdb_client_destroy */
141 int
142 sdb_client_connect(sdb_client_t *client, const char *username)
143 {
144 sdb_strbuf_t *buf;
145 ssize_t status;
146 uint32_t rstatus;
148 if ((! client) || (! client->address))
149 return -1;
151 if (client->fd >= 0)
152 return -1;
154 if (!strncasecmp(client->address, "unix:", strlen("unix:")))
155 connect_unixsock(client, client->address + strlen("unix:"));
156 else if (*client->address == '/')
157 connect_unixsock(client, client->address);
158 else {
159 sdb_log(SDB_LOG_ERR, "Unknown address type: %s", client->address);
160 return -1;
161 }
163 if (client->fd < 0)
164 return -1;
165 client->eof = 0;
167 /* XXX */
168 if (! username)
169 username = "";
171 status = sdb_client_send(client, SDB_CONNECTION_STARTUP,
172 (uint32_t)strlen(username), username);
173 if (status < 0) {
174 char errbuf[1024];
175 sdb_client_close(client);
176 sdb_log(SDB_LOG_ERR, "Failed to send STARTUP message to server: %s",
177 sdb_strerror(errno, errbuf, sizeof(errbuf)));
178 return (int)status;
179 }
181 buf = sdb_strbuf_create(64);
182 rstatus = 0;
183 status = sdb_client_recv(client, &rstatus, buf);
184 if ((status > 0) && (rstatus == SDB_CONNECTION_OK)) {
185 sdb_strbuf_destroy(buf);
186 return 0;
187 }
189 if (status < 0) {
190 char errbuf[1024];
191 sdb_log(SDB_LOG_ERR, "Failed to receive server response: %s",
192 sdb_strerror(errno, errbuf, sizeof(errbuf)));
193 }
194 else if (client->eof)
195 sdb_log(SDB_LOG_ERR, "Encountered end-of-file while waiting "
196 "for server response");
198 if (rstatus == SDB_CONNECTION_ERROR) {
199 sdb_log(SDB_LOG_ERR, "Access denied for user '%s': %s",
200 username, sdb_strbuf_string(buf));
201 status = -((int)rstatus);
202 }
203 else if (rstatus != SDB_CONNECTION_OK) {
204 sdb_log(SDB_LOG_ERR, "Received unsupported authentication request "
205 "(status %d) during startup", (int)rstatus);
206 status = -((int)rstatus);
207 }
209 sdb_client_close(client);
210 sdb_strbuf_destroy(buf);
211 return (int)status;
212 } /* sdb_client_connect */
214 int
215 sdb_client_sockfd(sdb_client_t *client)
216 {
217 if (! client)
218 return -1;
219 return client->fd;
220 } /* sdb_client_sockfd */
222 int
223 sdb_client_shutdown(sdb_client_t *client, int how)
224 {
225 if (! client) {
226 errno = ENOTSOCK;
227 return -1;
228 }
230 if (client->fd < 0) {
231 errno = EBADF;
232 return -1;
233 }
235 return shutdown(client->fd, how);
236 } /* sdb_client_shutdown */
238 void
239 sdb_client_close(sdb_client_t *client)
240 {
241 if (! client)
242 return;
244 close(client->fd);
245 client->fd = -1;
246 client->eof = 1;
247 } /* sdb_client_close */
249 ssize_t
250 sdb_client_send(sdb_client_t *client,
251 uint32_t cmd, uint32_t msg_len, const char *msg)
252 {
253 char buf[2 * sizeof(uint32_t) + msg_len];
255 if ((! client) || (! client->fd))
256 return -1;
257 if (sdb_proto_marshal(buf, sizeof(buf), cmd, msg_len, msg) < 0)
258 return -1;
260 return sdb_write(client->fd, sizeof(buf), buf);
261 } /* sdb_client_send */
263 ssize_t
264 sdb_client_recv(sdb_client_t *client,
265 uint32_t *code, sdb_strbuf_t *buf)
266 {
267 uint32_t rstatus = UINT32_MAX;
268 uint32_t rlen = UINT32_MAX;
270 size_t total = 0;
271 size_t req = 2 * sizeof(uint32_t);
273 size_t data_offset = sdb_strbuf_len(buf);
275 if (code)
276 *code = UINT32_MAX;
278 if ((! client) || (! client->fd) || (! buf)) {
279 errno = EBADF;
280 return -1;
281 }
283 while (42) {
284 ssize_t status;
286 if (sdb_select(client->fd, SDB_SELECTIN))
287 return -1;
289 errno = 0;
290 status = sdb_strbuf_read(buf, client->fd, req);
291 if (status < 0) {
292 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
293 continue;
294 return status;
295 }
296 else if (! status) {
297 client->eof = 1;
298 break;
299 }
301 total += (size_t)status;
303 if (total != req)
304 continue;
306 if (rstatus == UINT32_MAX) {
307 const char *str = sdb_strbuf_string(buf) + data_offset;
308 size_t len = sdb_strbuf_len(buf) - data_offset;
310 /* retrieve status and data len */
311 assert(len >= 2 * sizeof(uint32_t));
312 rstatus = sdb_proto_unmarshal_int(str, len);
313 rlen = sdb_proto_unmarshal_int(str + sizeof(rstatus),
314 len - sizeof(rstatus));
316 if (! rlen)
317 break;
319 req = (size_t)rlen;
320 total = 0;
321 }
322 else /* finished reading data */
323 break;
324 }
326 if (total != req) {
327 /* unexpected EOF; clear partially read data */
328 sdb_strbuf_skip(buf, data_offset, sdb_strbuf_len(buf));
329 return 0;
330 }
332 if (rstatus != UINT32_MAX)
333 /* remove status,len */
334 sdb_strbuf_skip(buf, data_offset, 2 * sizeof(rstatus));
336 if (code)
337 *code = rstatus;
339 return (ssize_t)total;
340 } /* sdb_client_recv */
342 bool
343 sdb_client_eof(sdb_client_t *client)
344 {
345 if ((! client) || (client->fd < 0))
346 return 1;
347 return client->eof;
348 } /* sdb_client_eof */
350 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */