]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lib/connection.c
lib: stricly-prevent use of a connection used to watch events
[thirdparty/lldpd.git] / src / lib / connection.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2012 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 <unistd.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23
24 #include "lldpctl.h"
25 #include "atom.h"
26 #include "../compat/compat.h"
27 #include "../ctl.h"
28 #include "../log.h"
29
30 const char*
31 lldpctl_get_default_transport(void)
32 {
33 return LLDPD_CTL_SOCKET;
34 }
35
36 /* Connect to the remote end */
37 static int
38 sync_connect(lldpctl_conn_t *lldpctl)
39 {
40 return ctl_connect(lldpctl->ctlname);
41 }
42
43 /* Synchronously send data to remote end. */
44 static ssize_t
45 sync_send(lldpctl_conn_t *lldpctl,
46 const uint8_t *data, size_t length, void *user_data)
47 {
48 struct lldpctl_conn_sync_t *conn = user_data;
49 ssize_t nb;
50
51 if (conn->fd == -1 &&
52 ((conn->fd = sync_connect(lldpctl)) == -1)) {
53 return LLDPCTL_ERR_CANNOT_CONNECT;
54 }
55
56 while ((nb = write(conn->fd, data, length)) == -1) {
57 if (errno == EAGAIN || errno == EINTR) continue;
58 return LLDPCTL_ERR_CALLBACK_FAILURE;
59 }
60 return nb;
61 }
62
63 /* Statically receive data from remote end. */
64 static ssize_t
65 sync_recv(lldpctl_conn_t *lldpctl,
66 const uint8_t *data, size_t length, void *user_data)
67 {
68 struct lldpctl_conn_sync_t *conn = user_data;
69 ssize_t nb;
70 size_t remain, offset = 0;
71
72 if (conn->fd == -1 &&
73 ((conn->fd = sync_connect(lldpctl)) == -1)) {
74 lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT;
75 return LLDPCTL_ERR_CANNOT_CONNECT;
76 }
77
78 remain = length;
79 do {
80 if ((nb = read(conn->fd, (unsigned char*)data + offset, remain)) == -1) {
81 if (errno == EAGAIN || errno == EINTR)
82 continue;
83 return LLDPCTL_ERR_CALLBACK_FAILURE;
84 }
85 remain -= nb;
86 offset += nb;
87 } while (remain > 0 && nb != 0);
88 return offset;
89 }
90
91 lldpctl_conn_t*
92 lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
93 {
94 return lldpctl_new_name(lldpctl_get_default_transport(), send, recv, user_data);
95 }
96
97 lldpctl_conn_t*
98 lldpctl_new_name(const char *ctlname, lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
99 {
100 lldpctl_conn_t *conn = NULL;
101 struct lldpctl_conn_sync_t *data = NULL;
102
103 /* Both callbacks are mandatory or should be NULL. */
104 if (send && !recv) return NULL;
105 if (recv && !send) return NULL;
106
107 if ((conn = calloc(1, sizeof(lldpctl_conn_t))) == NULL)
108 return NULL;
109
110 conn->ctlname = strdup(ctlname);
111 if (conn->ctlname == NULL) {
112 free(conn);
113 return NULL;
114 }
115 if (!send && !recv) {
116 if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) {
117 free(conn->ctlname);
118 free(conn);
119 return NULL;
120 }
121 data->fd = -1;
122 conn->send = sync_send;
123 conn->recv = sync_recv;
124 conn->user_data = data;
125 } else {
126 conn->send = send;
127 conn->recv = recv;
128 conn->user_data = user_data;
129 }
130
131 return conn;
132 }
133
134 int
135 lldpctl_release(lldpctl_conn_t *conn)
136 {
137 if (conn == NULL) return 0;
138 free(conn->ctlname);
139 if (conn->send == sync_send) {
140 struct lldpctl_conn_sync_t *data = conn->user_data;
141 if (data->fd != -1) close(data->fd);
142 free(conn->user_data);
143 }
144 free(conn->input_buffer);
145 free(conn->output_buffer);
146 free(conn);
147 return 0;
148 }
149
150 /**
151 * Request some bytes if they are not already here.
152 *
153 * @param conn The connection to lldpd.
154 * @param length The number of requested bytes.
155 * @return A negative integer if we can't have the bytes or the number of bytes we got.
156 */
157 ssize_t
158 _lldpctl_needs(lldpctl_conn_t *conn, size_t length)
159 {
160 uint8_t *buffer = NULL;
161 ssize_t rc;
162
163 if ((buffer = malloc(length)) == NULL)
164 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
165 rc = conn->recv(conn, buffer, length, conn->user_data);
166 if (rc < 0) {
167 free(buffer);
168 return SET_ERROR(conn, rc);
169 }
170 if (rc == 0) {
171 free(buffer);
172 return SET_ERROR(conn, LLDPCTL_ERR_EOF);
173 }
174 rc = lldpctl_recv(conn, buffer, rc);
175 free(buffer);
176 if (rc < 0)
177 return SET_ERROR(conn, rc);
178 RESET_ERROR(conn);
179 return rc;
180 }
181
182 static int
183 check_for_notification(lldpctl_conn_t *conn)
184 {
185 struct lldpd_neighbor_change *change;
186 void *p;
187 int rc;
188 lldpctl_change_t type;
189 lldpctl_atom_t *interface = NULL, *neighbor = NULL;
190 rc = ctl_msg_recv_unserialized(&conn->input_buffer,
191 &conn->input_buffer_len,
192 NOTIFICATION,
193 &p,
194 &MARSHAL_INFO(lldpd_neighbor_change));
195 if (rc != 0) return rc;
196 change = p;
197
198 /* We have a notification, call the callback */
199 if (conn->watch_cb) {
200 switch (change->state) {
201 case NEIGHBOR_CHANGE_DELETED: type = lldpctl_c_deleted; break;
202 case NEIGHBOR_CHANGE_ADDED: type = lldpctl_c_added; break;
203 case NEIGHBOR_CHANGE_UPDATED: type = lldpctl_c_updated; break;
204 default:
205 log_warnx("control", "unknown notification type (%d)",
206 change->state);
207 goto end;
208 }
209 interface = _lldpctl_new_atom(conn, atom_interface,
210 change->ifname);
211 if (interface == NULL) goto end;
212 neighbor = _lldpctl_new_atom(conn, atom_port, 0,
213 NULL, change->neighbor, NULL);
214 if (neighbor == NULL) goto end;
215 conn->watch_cb(conn, type, interface, neighbor, conn->watch_data);
216 conn->watch_triggered = 1;
217 goto end;
218 }
219
220 end:
221 if (interface) lldpctl_atom_dec_ref(interface);
222 if (neighbor) lldpctl_atom_dec_ref(neighbor);
223 else {
224 lldpd_chassis_cleanup(change->neighbor->p_chassis, 1);
225 lldpd_port_cleanup(change->neighbor, 1);
226 free(change->neighbor);
227 }
228 free(change->ifname);
229 free(change);
230
231 /* Indicate if more data remains in the buffer for processing */
232 return (rc);
233 }
234
235 ssize_t
236 lldpctl_recv(lldpctl_conn_t *conn, const uint8_t *data, size_t length)
237 {
238
239 RESET_ERROR(conn);
240
241 if (length == 0) return 0;
242
243 /* Received data should be appended to the input buffer. */
244 if (conn->input_buffer == NULL) {
245 conn->input_buffer_len = 0;
246 if ((conn->input_buffer = malloc(length)) == NULL)
247 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
248 } else {
249 uint8_t *new = realloc(conn->input_buffer, conn->input_buffer_len + length);
250 if (new == NULL)
251 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
252 conn->input_buffer = new;
253 }
254 memcpy(conn->input_buffer + conn->input_buffer_len, data, length);
255 conn->input_buffer_len += length;
256
257 /* Read all notifications */
258 while(!check_for_notification(conn));
259
260 RESET_ERROR(conn);
261
262 return conn->input_buffer_len;
263 }
264
265 int
266 lldpctl_process_conn_buffer(lldpctl_conn_t *conn)
267 {
268 int rc;
269
270 rc = check_for_notification(conn);
271
272 RESET_ERROR(conn);
273
274 return rc;
275 }
276
277 ssize_t
278 lldpctl_send(lldpctl_conn_t *conn)
279 {
280 /* Send waiting data. */
281 ssize_t rc;
282
283 RESET_ERROR(conn);
284
285 if (!conn->output_buffer) return 0;
286 rc = conn->send(conn,
287 conn->output_buffer, conn->output_buffer_len,
288 conn->user_data);
289 if (rc < 0) return SET_ERROR(conn, rc);
290
291 /* "Shrink" the output buffer. */
292 if (rc == conn->output_buffer_len) {
293 free(conn->output_buffer);
294 conn->output_buffer = NULL;
295 conn->output_buffer_len = 0;
296 RESET_ERROR(conn);
297 return rc;
298 }
299 conn->output_buffer_len -= rc;
300 memmove(conn->output_buffer, conn->output_buffer + rc, conn->output_buffer_len);
301 /* We don't shrink the buffer. It will be either freed or shrinked later */
302 RESET_ERROR(conn);
303 return rc;
304 }