Line | Count | Source |
1 | | /* -*- mode: c; c-file-style: "openbsd" -*- */ |
2 | | /* |
3 | | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> |
4 | | * |
5 | | * Permission to use, copy, modify, and/or distribute this software for any |
6 | | * purpose with or without fee is hereby granted, provided that the above |
7 | | * copyright notice and this permission notice appear in all copies. |
8 | | * |
9 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | | */ |
17 | | |
18 | | #include <stdlib.h> |
19 | | #include <unistd.h> |
20 | | #include <fcntl.h> |
21 | | #include <errno.h> |
22 | | #include <sys/types.h> |
23 | | #include <sys/socket.h> |
24 | | #include <sys/un.h> |
25 | | #include <string.h> |
26 | | |
27 | | #include "ctl.h" |
28 | | #include "marshal.h" |
29 | | #include "log.h" |
30 | | #include "compat/compat.h" |
31 | | |
32 | | /** |
33 | | * Create a new listening Unix socket for control protocol. |
34 | | * |
35 | | * @param name The name of the Unix socket. |
36 | | * @return The socket when successful, -1 otherwise. |
37 | | */ |
38 | | int |
39 | | ctl_create(const char *name) |
40 | 0 | { |
41 | 0 | int s; |
42 | 0 | struct sockaddr_un su; |
43 | 0 | int rc; |
44 | |
|
45 | 0 | log_debug("control", "create control socket %s", name); |
46 | |
|
47 | 0 | if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) return -1; |
48 | 0 | if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { |
49 | 0 | close(s); |
50 | 0 | return -1; |
51 | 0 | } |
52 | 0 | su.sun_family = AF_UNIX; |
53 | 0 | strlcpy(su.sun_path, name, sizeof(su.sun_path)); |
54 | 0 | if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { |
55 | 0 | rc = errno; |
56 | 0 | close(s); |
57 | 0 | errno = rc; |
58 | 0 | return -1; |
59 | 0 | } |
60 | | |
61 | 0 | log_debug("control", "listen to control socket %s", name); |
62 | 0 | if (listen(s, 5) == -1) { |
63 | 0 | rc = errno; |
64 | 0 | close(s); |
65 | 0 | errno = rc; |
66 | 0 | log_debug("control", "cannot listen to control socket %s", name); |
67 | 0 | return -1; |
68 | 0 | } |
69 | 0 | return s; |
70 | 0 | } |
71 | | |
72 | | /** |
73 | | * Connect to the control Unix socket. |
74 | | * |
75 | | * @param name The name of the Unix socket. |
76 | | * @return The socket when successful, -1 otherwise. |
77 | | */ |
78 | | int |
79 | | ctl_connect(const char *name) |
80 | 0 | { |
81 | 0 | int s; |
82 | 0 | struct sockaddr_un su; |
83 | 0 | int rc; |
84 | |
|
85 | 0 | log_debug("control", "connect to control socket %s", name); |
86 | |
|
87 | 0 | if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) return -1; |
88 | 0 | su.sun_family = AF_UNIX; |
89 | 0 | strlcpy(su.sun_path, name, sizeof(su.sun_path)); |
90 | 0 | if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { |
91 | 0 | rc = errno; |
92 | 0 | log_warn("control", "unable to connect to socket %s", name); |
93 | 0 | close(s); |
94 | 0 | errno = rc; |
95 | 0 | return -1; |
96 | 0 | } |
97 | 0 | return s; |
98 | 0 | } |
99 | | |
100 | | /** |
101 | | * Remove the control Unix socket. |
102 | | * |
103 | | * @param name The name of the Unix socket. |
104 | | */ |
105 | | void |
106 | | ctl_cleanup(const char *name) |
107 | 0 | { |
108 | 0 | log_debug("control", "cleanup control socket"); |
109 | 0 | if (unlink(name) == -1) log_warn("control", "unable to unlink %s", name); |
110 | 0 | } |
111 | | |
112 | | /** |
113 | | * Serialize and "send" a structure through the control protocol. |
114 | | * |
115 | | * This function does not really send the message but outputs it to a buffer. |
116 | | * |
117 | | * @param output_buffer A pointer to a buffer to which the message will be |
118 | | * appended. Can be @c NULL. In this case, the buffer will |
119 | | * be allocated. |
120 | | * @param[in,out] output_len The length of the provided buffer. Will be updated |
121 | | * with the new length |
122 | | * @param type The type of message we want to send. |
123 | | * @param t The structure to be serialized and sent. |
124 | | * @param mi The appropriate marshal structure for serialization. |
125 | | * @return -1 in case of failure, 0 in case of success. |
126 | | * |
127 | | * Make sure this function logic matches the server-side one: @c levent_ctl_recv(). |
128 | | */ |
129 | | int |
130 | | ctl_msg_send_unserialized(uint8_t **output_buffer, size_t *output_len, |
131 | | enum hmsg_type type, void *t, struct marshal_info *mi) |
132 | 0 | { |
133 | 0 | ssize_t len = 0, newlen; |
134 | 0 | void *buffer = NULL; |
135 | |
|
136 | 0 | log_debug("control", "send a message through control socket"); |
137 | 0 | if (t) { |
138 | 0 | len = marshal_serialize_(mi, t, &buffer, 0, NULL, 0); |
139 | 0 | if (len <= 0) { |
140 | 0 | log_warnx("control", "unable to serialize data"); |
141 | 0 | return -1; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | 0 | newlen = len + sizeof(struct hmsg_header); |
146 | |
|
147 | 0 | if (*output_buffer == NULL) { |
148 | 0 | *output_len = 0; |
149 | 0 | if ((*output_buffer = malloc(newlen)) == NULL) { |
150 | 0 | log_warn("control", "no memory available"); |
151 | 0 | free(buffer); |
152 | 0 | return -1; |
153 | 0 | } |
154 | 0 | } else { |
155 | 0 | void *new = realloc(*output_buffer, *output_len + newlen); |
156 | 0 | if (new == NULL) { |
157 | 0 | log_warn("control", "no memory available"); |
158 | 0 | free(buffer); |
159 | 0 | return -1; |
160 | 0 | } |
161 | 0 | *output_buffer = new; |
162 | 0 | } |
163 | | |
164 | 0 | struct hmsg_header hdr; |
165 | 0 | memset(&hdr, 0, sizeof(struct hmsg_header)); |
166 | 0 | hdr.type = type; |
167 | 0 | hdr.len = len; |
168 | 0 | memcpy(*output_buffer + *output_len, &hdr, sizeof(struct hmsg_header)); |
169 | 0 | if (t) |
170 | 0 | memcpy(*output_buffer + *output_len + sizeof(struct hmsg_header), |
171 | 0 | buffer, len); |
172 | 0 | *output_len += newlen; |
173 | 0 | free(buffer); |
174 | 0 | return 0; |
175 | 0 | } |
176 | | |
177 | | /** |
178 | | * "Receive" and unserialize a structure through the control protocol. |
179 | | * |
180 | | * Like @c ctl_msg_send_unserialized(), this function uses buffer to receive the |
181 | | * incoming message. |
182 | | * |
183 | | * @param[in,out] input_buffer The buffer with the incoming message. Will be |
184 | | * updated once the message has been unserialized to |
185 | | * point to the remaining of the message or will be |
186 | | * freed if all the buffer has been consumed. Can be |
187 | | * @c NULL. |
188 | | * @param[in,out] input_len The length of the provided buffer. Will be updated |
189 | | * to the length of remaining data once the message |
190 | | * has been unserialized. |
191 | | * @param expected_type The expected message type. |
192 | | * @param[out] t Will contain a pointer to the unserialized structure. |
193 | | * Can be @c NULL if we don't want to store the |
194 | | * answer. |
195 | | * @param mi The appropriate marshal structure for unserialization. |
196 | | * |
197 | | * @return -1 in case of error, 0 in case of success and the number of bytes we |
198 | | * request to complete unserialization. |
199 | | * |
200 | | * When requesting a notification, the input buffer is left untouched if we |
201 | | * don't get one and we fail silently. |
202 | | */ |
203 | | size_t |
204 | | ctl_msg_recv_unserialized(uint8_t **input_buffer, size_t *input_len, |
205 | | enum hmsg_type expected_type, void **t, struct marshal_info *mi) |
206 | 0 | { |
207 | 0 | struct hmsg_header hdr; |
208 | 0 | int rc = -1; |
209 | |
|
210 | 0 | if (*input_buffer == NULL || *input_len < sizeof(struct hmsg_header)) { |
211 | | /* Not enough data. */ |
212 | 0 | return sizeof(struct hmsg_header) - *input_len; |
213 | 0 | } |
214 | | |
215 | 0 | log_debug("control", "receive a message through control socket"); |
216 | 0 | memcpy(&hdr, *input_buffer, sizeof(struct hmsg_header)); |
217 | 0 | if (hdr.len > HMSG_MAX_SIZE) { |
218 | 0 | log_warnx("control", "message received is too large"); |
219 | | /* We discard the whole buffer */ |
220 | 0 | free(*input_buffer); |
221 | 0 | *input_buffer = NULL; |
222 | 0 | *input_len = 0; |
223 | 0 | return -1; |
224 | 0 | } |
225 | 0 | if (*input_len < sizeof(struct hmsg_header) + hdr.len) { |
226 | | /* Not enough data. */ |
227 | 0 | return sizeof(struct hmsg_header) + hdr.len - *input_len; |
228 | 0 | } |
229 | 0 | if (hdr.type != expected_type) { |
230 | 0 | if (expected_type == NOTIFICATION) return -1; |
231 | 0 | log_warnx("control", |
232 | 0 | "incorrect received message type (expected: %d, received: %d)", |
233 | 0 | expected_type, hdr.type); |
234 | 0 | goto end; |
235 | 0 | } |
236 | | |
237 | 0 | if (t && !hdr.len) { |
238 | 0 | log_warnx("control", "no payload available in answer"); |
239 | 0 | goto end; |
240 | 0 | } |
241 | 0 | if (t) { |
242 | | /* We have data to unserialize. */ |
243 | 0 | if (marshal_unserialize_(mi, *input_buffer + sizeof(struct hmsg_header), |
244 | 0 | hdr.len, t, NULL, 0, 0) <= 0) { |
245 | 0 | log_warnx("control", "unable to deserialize received data"); |
246 | 0 | goto end; |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | 0 | rc = 0; |
251 | 0 | end: |
252 | | /* Discard input buffer */ |
253 | 0 | *input_len -= sizeof(struct hmsg_header) + hdr.len; |
254 | 0 | if (*input_len == 0) { |
255 | 0 | free(*input_buffer); |
256 | 0 | *input_buffer = NULL; |
257 | 0 | } else |
258 | 0 | memmove(*input_buffer, |
259 | 0 | *input_buffer + sizeof(struct hmsg_header) + hdr.len, *input_len); |
260 | 0 | return rc; |
261 | 0 | } |