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 <errno.h>
41 #include <limits.h>
43 #include <stdlib.h>
45 #include <string.h>
46 #include <strings.h>
48 #include <unistd.h>
50 #include <sys/socket.h>
51 #include <sys/un.h>
53 /*
54 * private data types
55 */
57 struct sdb_client {
58 char *address;
59 int fd;
60 bool eof;
61 };
63 /*
64 * private helper functions
65 */
67 static int
68 connect_unixsock(sdb_client_t *client, const char *address)
69 {
70 struct sockaddr_un sa;
72 client->fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
73 if (client->fd < 0) {
74 char errbuf[1024];
75 sdb_log(SDB_LOG_ERR, "Failed to open socket: %s",
76 sdb_strerror(errno, errbuf, sizeof(errbuf)));
77 return -1;
78 }
80 sa.sun_family = AF_UNIX;
81 strncpy(sa.sun_path, address, sizeof(sa.sun_path));
82 sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
84 if (connect(client->fd, (struct sockaddr *)&sa, sizeof(sa))) {
85 char errbuf[1024];
86 sdb_client_close(client);
87 sdb_log(SDB_LOG_ERR, "Failed to connect to '%s': %s",
88 sa.sun_path, sdb_strerror(errno, errbuf, sizeof(errbuf)));
89 return -1;
90 }
91 return client->fd;
92 } /* connect_unixsock */
94 /*
95 * public API
96 */
98 sdb_client_t *
99 sdb_client_create(const char *address)
100 {
101 sdb_client_t *client;
103 if (! address)
104 return NULL;
106 client = malloc(sizeof(*client));
107 if (! client) {
108 sdb_log(SDB_LOG_ERR, "Out of memory");
109 return NULL;
110 }
111 memset(client, 0, sizeof(*client));
112 client->fd = -1;
113 client->eof = 1;
115 client->address = strdup(address);
116 if (! client->address) {
117 sdb_client_destroy(client);
118 sdb_log(SDB_LOG_ERR, "Out of memory");
119 return NULL;
120 }
122 return client;
123 } /* sdb_client_create */
125 void
126 sdb_client_destroy(sdb_client_t *client)
127 {
128 if (! client)
129 return;
131 sdb_client_close(client);
133 if (client->address)
134 free(client->address);
135 client->address = NULL;
137 free(client);
138 } /* sdb_client_destroy */
140 int
141 sdb_client_connect(sdb_client_t *client, const char *username)
142 {
143 sdb_strbuf_t *buf;
144 ssize_t status;
145 uint32_t rstatus;
147 if ((! client) || (! client->address))
148 return -1;
150 if (client->fd >= 0)
151 return -1;
153 if (!strncasecmp(client->address, "unix:", strlen("unix:")))
154 connect_unixsock(client, client->address + strlen("unix:"));
155 else if (*client->address == '/')
156 connect_unixsock(client, client->address);
157 else {
158 sdb_log(SDB_LOG_ERR, "Unknown address type: %s", client->address);
159 return -1;
160 }
162 if (client->fd < 0)
163 return -1;
164 client->eof = 0;
166 /* XXX */
167 if (! username)
168 username = "";
170 status = sdb_client_send(client, SDB_CONNECTION_STARTUP,
171 (uint32_t)strlen(username), username);
172 if (status < 0) {
173 char errbuf[1024];
174 sdb_client_close(client);
175 sdb_log(SDB_LOG_ERR, "Failed to send STARTUP message to server: %s",
176 sdb_strerror(errno, errbuf, sizeof(errbuf)));
177 return (int)status;
178 }
180 buf = sdb_strbuf_create(64);
181 rstatus = 0;
182 status = sdb_client_recv(client, &rstatus, buf);
183 if ((status > 0) && (rstatus == SDB_CONNECTION_OK)) {
184 sdb_strbuf_destroy(buf);
185 return 0;
186 }
188 if (status < 0) {
189 char errbuf[1024];
190 sdb_log(SDB_LOG_ERR, "Failed to receive server response: %s",
191 sdb_strerror(errno, errbuf, sizeof(errbuf)));
192 }
193 else if (client->eof)
194 sdb_log(SDB_LOG_ERR, "Encountered end-of-file while waiting "
195 "for server response");
197 if (rstatus == SDB_CONNECTION_ERROR) {
198 sdb_log(SDB_LOG_ERR, "Access denied for user '%s': %s",
199 username, sdb_strbuf_string(buf));
200 status = -((int)rstatus);
201 }
202 else if (rstatus != SDB_CONNECTION_OK) {
203 sdb_log(SDB_LOG_ERR, "Received unsupported authentication request "
204 "(status %d) during startup", (int)rstatus);
205 status = -((int)rstatus);
206 }
208 sdb_client_close(client);
209 sdb_strbuf_destroy(buf);
210 return (int)status;
211 } /* sdb_client_connect */
213 int
214 sdb_client_sockfd(sdb_client_t *client)
215 {
216 if (! client)
217 return -1;
218 return client->fd;
219 } /* sdb_client_sockfd */
221 int
222 sdb_client_shutdown(sdb_client_t *client, int how)
223 {
224 if (! client) {
225 errno = ENOTSOCK;
226 return -1;
227 }
229 if (client->fd < 0) {
230 errno = EBADF;
231 return -1;
232 }
234 return shutdown(client->fd, how);
235 } /* sdb_client_shutdown */
237 void
238 sdb_client_close(sdb_client_t *client)
239 {
240 if (! client)
241 return;
243 close(client->fd);
244 client->fd = -1;
245 client->eof = 1;
246 } /* sdb_client_close */
248 ssize_t
249 sdb_client_send(sdb_client_t *client,
250 uint32_t cmd, uint32_t msg_len, const char *msg)
251 {
252 char buf[2 * sizeof(uint32_t) + msg_len];
254 if ((! client) || (! client->fd))
255 return -1;
256 if (sdb_proto_marshal(buf, sizeof(buf), cmd, msg_len, msg) < 0)
257 return -1;
259 return sdb_write(client->fd, sizeof(buf), buf);
260 } /* sdb_client_send */
262 ssize_t
263 sdb_client_recv(sdb_client_t *client,
264 uint32_t *code, sdb_strbuf_t *buf)
265 {
266 uint32_t rstatus = UINT32_MAX;
267 uint32_t rlen = UINT32_MAX;
269 size_t total = 0;
270 size_t req = 2 * sizeof(uint32_t);
272 size_t data_offset = sdb_strbuf_len(buf);
274 if (code)
275 *code = UINT32_MAX;
277 if ((! client) || (! client->fd) || (! buf)) {
278 errno = EBADF;
279 return -1;
280 }
282 while (42) {
283 ssize_t status;
285 if (sdb_select(client->fd, SDB_SELECTIN))
286 return -1;
288 errno = 0;
289 status = sdb_strbuf_read(buf, client->fd, req);
290 if (status < 0) {
291 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
292 continue;
293 return status;
294 }
295 else if (! status) {
296 client->eof = 1;
297 break;
298 }
300 total += (size_t)status;
302 if (total != req)
303 continue;
305 if (rstatus == UINT32_MAX) {
306 /* retrieve status and data len */
307 rstatus = sdb_proto_get_int(buf, data_offset);
308 rlen = sdb_proto_get_int(buf, data_offset + sizeof(rstatus));
310 if (! rlen)
311 break;
313 req = (size_t)rlen;
314 total = 0;
315 }
316 else /* finished reading data */
317 break;
318 }
320 if (total != req) {
321 /* unexpected EOF; clear partially read data */
322 sdb_strbuf_skip(buf, data_offset, sdb_strbuf_len(buf));
323 return 0;
324 }
326 if (rstatus != UINT32_MAX)
327 /* remove status,len */
328 sdb_strbuf_skip(buf, data_offset, 2 * sizeof(rstatus));
330 if (code)
331 *code = rstatus;
333 return (ssize_t)total;
334 } /* sdb_client_recv */
336 bool
337 sdb_client_eof(sdb_client_t *client)
338 {
339 if ((! client) || (client->fd < 0))
340 return 1;
341 return client->eof;
342 } /* sdb_client_eof */
344 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */