quagga: refactor zclient_read to be much more readable and safer
[olsrd.git] / lib / quagga / src / client.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 /* -------------------------------------------------------------------------
47  * File               : client.c
48  * Description        : zebra client functions
49  * ------------------------------------------------------------------------- */
50
51 #define HAVE_SOCKLEN_T
52
53 #include <sys/un.h>
54 #include <unistd.h>
55 #include <fcntl.h>
56 #include "defs.h"
57 #include "olsr.h"
58 #include "log.h"
59 #include "routing_table.h"
60 #include "common/autobuf.h"
61
62 #include "common.h"
63 #include "quagga.h"
64 #include "packet.h"
65 #include "client.h"
66
67 static void *my_realloc(void *, size_t, const char *);
68 static void zclient_connect(void);
69
70 static void *
71 my_realloc(void *buf, size_t s, const char *c)
72 {
73
74   buf = realloc(buf, s);
75   if (!buf) {
76     char buf2[1024];
77     snprintf(buf2, sizeof(buf2), "QUAGGA: Out of memory (%s): %s", c, strerror(errno));
78     olsr_exit(buf2, EXIT_FAILURE);
79   }
80
81   return buf;
82 }
83
84 static void
85 zclient_connect(void)
86 {
87   int ret;
88
89   union {
90     struct sockaddr_in sin;
91     struct sockaddr_un sun;
92   } sockaddr;
93
94   if (close(zebra.sock) < 0)
95     olsr_exit("QUAGGA: Could not close socket", EXIT_FAILURE);
96
97   zebra.sock = socket(zebra.port ? AF_INET : AF_UNIX, SOCK_STREAM, 0);
98
99   if (zebra.sock < 0)
100     olsr_exit("QUAGGA: Could not create socket", EXIT_FAILURE);
101
102   memset(&sockaddr, 0, sizeof sockaddr);
103
104   if (zebra.port) {
105     sockaddr.sin.sin_family = AF_INET;
106     sockaddr.sin.sin_port = htons(zebra.port);
107     sockaddr.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
108     ret = connect(zebra.sock, (struct sockaddr *)&sockaddr.sin, sizeof sockaddr.sin);
109   } else {
110     sockaddr.sun.sun_family = AF_UNIX;
111     strscpy(sockaddr.sun.sun_path, zebra.sockpath, sizeof(sockaddr.sun.sun_path));
112     ret = connect(zebra.sock, (struct sockaddr *)&sockaddr.sun, sizeof sockaddr.sun);
113   }
114
115   if (ret < 0)
116     zebra.status &= ~STATUS_CONNECTED;
117   else
118     zebra.status |= STATUS_CONNECTED;
119
120 }
121
122 void
123 zclient_reconnect(void)
124 {
125   struct rt_entry *tmp;
126
127   zclient_connect();
128   if (!(zebra.status & STATUS_CONNECTED))
129     return;                     // try again next time
130
131   zebra_hello(ZEBRA_HELLO);
132   if (zebra.options & OPTION_EXPORT) {
133     OLSR_FOR_ALL_RT_ENTRIES(tmp) {
134       zebra_addroute(tmp);
135     }
136     OLSR_FOR_ALL_RT_ENTRIES_END(tmp);
137   }
138   zebra_redistribute(ZEBRA_REDISTRIBUTE_ADD);
139
140 }
141
142 int
143 zclient_write(unsigned char *options)
144 {
145   unsigned char *pnt;
146   uint16_t len;
147   int ret;
148
149   if (!(zebra.status & STATUS_CONNECTED))
150     return 0;
151
152   pnt = options;
153   memcpy(&len, pnt, sizeof len);
154
155   len = ntohs(len);
156
157   do {
158     ret = write(zebra.sock, pnt, len);
159     if (ret < 0) {
160       if ((errno == EINTR) || (errno == EAGAIN)) {
161         errno = 0;
162         ret = 0;
163         continue;
164       } else {
165         OLSR_PRINTF(1, "(QUAGGA) Disconnected from zebra.\n");
166         zebra.status &= ~STATUS_CONNECTED;
167         /* TODO: Remove HNAs added from redistribution */
168         free(options);
169         return -1;
170       }
171     }
172     pnt = pnt + ret;
173   }
174   while ((len -= ret));
175   free(options);
176
177   return 0;
178 }
179
180 unsigned char *
181 zclient_read(ssize_t * size)
182 {
183   unsigned char *buf = NULL;
184   ssize_t bufsize = 0;
185   uint16_t offset = 0;
186   int socket_flags_saved = fcntl(zebra.sock, F_GETFL);
187
188   /* initialise size */
189   *size = 0;
190
191   /* set non-blocking on the socket */
192   (void) fcntl(zebra.sock, F_SETFL, socket_flags_saved | O_NONBLOCK);
193
194   /* read complete packets */
195   do {
196     ssize_t bytes_received;
197     uint16_t packet_length;
198
199     /* (re)allocate buffer */
200     if (*size == bufsize) {
201       ssize_t start = bufsize;
202       bufsize += AUTOBUFCHUNK;
203       buf = my_realloc(buf, bufsize, "grow read buffer");
204       memset(&buf[start], 0, AUTOBUFCHUNK);
205     }
206
207     /* read from socket */
208     bytes_received = read(zebra.sock, &buf[*size], bufsize - *size);
209
210     /* handle invalid packet */
211     if (!bytes_received) {
212       goto error_out;
213     }
214
215     /* handle no data available */
216     if (bytes_received < 0) {
217       /* handle disconnect */
218       if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { // oops - we got disconnected
219         OLSR_PRINTF(1, "(QUAGGA) Disconnected from zebra\n");
220         zebra.status &= ~STATUS_CONNECTED;
221         /* TODO: Remove HNAs added from redistribution */
222       }
223
224       goto error_out;
225     }
226
227     *size += bytes_received;
228
229     /* detect zebra packet fragmentation */
230     while (*size >= (ssize_t) (offset + sizeof(packet_length))) {
231       packet_length = ntohs(*((uint16_t *) &buf[offset]));
232       offset += packet_length;
233     }
234   } while (*size != offset);
235
236   out: (void) fcntl(zebra.sock, F_SETFL, socket_flags_saved);
237   return buf;
238
239   error_out: free(buf);
240   buf = NULL;
241   *size = 0;
242   goto out;
243 }
244
245 /*
246  * Local Variables:
247  * c-basic-offset: 2
248  * indent-tabs-mode: nil
249  * End:
250  */