An off-by-one malloc size was corrupting the installer's memory,
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / src / lib / libdfui / conn_tcp.c
1 /*
2  *
3  * Copyright (c) 2004 Scott Ullrich <GeekGod@GeekGod.com> 
4  * Portions Copyright (c) 2004 Chris Pressey <cpressey@catseye.mine.nu>
5  *
6  * Copyright (c) 2004 The DragonFly Project.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The DragonFly Project
10  * by Scott Ullrich and Chris Pressey (see above for e-mail addresses).
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  *
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in
21  *    the documentation and/or other materials provided with the
22  *    distribution.
23  *
24  * 3. Neither the name of The DragonFly Project nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific, prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
32  * COPYRIGHT HOLDERS, CONTRIBUTORS OR VOICES IN THE AUTHOR'S HEAD
33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY
34  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
35  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
36  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
37  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
38  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
41  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
42  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
44  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  */
49
50 /*
51  * conn_tcp.c
52  * $Id: conn_tcp.c,v 1.16 2005/02/06 19:53:19 cpressey Exp $
53  */
54
55 #include "system.h"
56 #ifdef HAS_TCP
57
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/time.h>
61 #include <sys/errno.h>
62 #include <sys/types.h>
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
66
67 #include <err.h>
68 #include <errno.h>
69 #include <stdarg.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74
75 #include <aura/buffer.h>
76
77 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS
78 #include "dfui.h"
79 #undef  NEEDS_DFUI_STRUCTURE_DEFINITIONS
80 #include "encoding.h"
81 #include "conn_tcp.h"
82 #include "dump.h"
83
84 /***** BACKEND ******/
85
86 /** High Level **/
87
88 /*
89  * Connect to the frontend.
90  */
91 dfui_err_t
92 dfui_tcp_be_start(struct dfui_connection *c)
93 {
94         struct sockaddr_in servaddr;
95         int server_port;
96         int tru = 1;
97
98         server_port = atoi(c->rendezvous);
99
100         /*
101          * Create the tcp socket
102          */
103         errno = 0;
104         if ((T_TCP(c)->listen_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
105                 return(DFUI_FAILURE);
106         dfui_debug("LISTEN_SOCKET<<%d>>\n", T_TCP(c)->listen_sd);
107
108         if (setsockopt(T_TCP(c)->listen_sd, SOL_SOCKET, SO_REUSEADDR,
109             &tru, sizeof(tru)) == -1) {
110                 return(DFUI_FAILURE);           
111         }
112
113         bzero(&servaddr, sizeof(servaddr));
114         servaddr.sin_family = AF_INET;
115         servaddr.sin_port = htons(server_port);
116         switch(inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr)) {
117         case 0:
118                 warnx("inet_pton(): address not parseable");
119                 return(DFUI_FAILURE);   
120         case 1:
121                 break;
122         default:
123                 warn("inet_pton()");
124                 return(DFUI_FAILURE);
125         }
126         
127         if (bind(T_TCP(c)->listen_sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
128                 warn("bind()");
129                 return(DFUI_FAILURE);
130         }
131         dfui_debug("BOUND_ON<<%d>>\n", T_TCP(c)->listen_sd);
132         if (listen(T_TCP(c)->listen_sd, 0) == -1)
133                 return(DFUI_FAILURE);
134         dfui_debug("LISTENING_ON<<%d>>\n", T_TCP(c)->listen_sd);
135         /* at this point we should be listening on the rendezvous port */
136         return(DFUI_SUCCESS);
137 }
138
139 /*
140  * Tell the frontend that we're done and disconnect from it.
141  */
142 dfui_err_t
143 dfui_tcp_be_stop(struct dfui_connection *c)
144 {
145         if (dfui_tcp_be_ll_exchange(c, DFUI_BE_MSG_STOP, "")) {
146                 close(T_TCP(c)->listen_sd);
147                 close(T_TCP(c)->connected_sd);
148                 fclose(T_TCP(c)->stream);
149                 return(DFUI_SUCCESS);
150         } else
151                 return(DFUI_FAILURE);
152 }
153
154 /** Low Level **/
155
156 /*
157  * Exchange a message with the frontend.  This involves two receive()/reply()
158  * cycles: one to provide our message, one to get a reply from the frontend.
159  *
160  * Note that this does not immediately send the message to the frontend -
161  * it can't, because we're a service and it's a client.  What it does is
162  * keep the message handy and wait for a frontend request to come in.  It
163  * then replies to that request with our message.
164  *
165  * The protocol looks something like the following, using the PRESENT and
166  * SUBMIT exchange as an example:
167  *
168  * frontend (client) | backend (service)
169  * ------------------+------------------
170  *
171  *                                     [stage 1]
172  * READY            -->                ll_receive()
173  *                 <--  PRESENT(form)  ll_reply()
174  *
175  *                                     [stage 2]
176  * SUBMIT(form)     -->                ll_receive()
177  *                 <--  READY          ll_reply()
178  *
179  * Each of those exchanges is a pair of calls, on our end, to
180  * dfui_tcp_be_ll_receive() and dfui_npipe_be_ll_reply().
181  *
182  * The set of messages that the client can pass us is determined by
183  * the conversation state:
184  *
185  *   o  In stage 1, only READY and ABORT are meaningful.
186  *   o  After a PRESENT, the messages SUBMIT and ABORT are meaningul
187  *      in stage 2.
188  *   o  During a PROG_*, the messages CONTINUE, CANCEL, and ABORT
189  *      are meaningful in stage 2.
190  *
191  * If the frontend sends us with READY in stage 2, we assume it has
192  * fallen out of sync, so we send the same initial reply again, going
193  * back to stage 1 as it were.
194  *
195  * After this call, the message is available in c->ebuf.
196  */
197 dfui_err_t
198 dfui_tcp_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg)
199 {
200         char *fmsg;
201
202         /*
203          * Construct our message to send.
204          */
205
206         fmsg = malloc(strlen(msg) + 2);
207         fmsg[0] = msgtype;
208         strcpy(fmsg + 1, msg);
209
210         /*
211          * Get the frontend's message.
212          */
213
214         dfui_tcp_be_ll_receive(c);
215
216         /*
217          * Frontend message should have been either READY or ABORT.
218          * If ABORT, we get out of here pronto.
219          */
220
221         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
222                 free(fmsg);
223                 return(DFUI_FAILURE);
224         }
225
226         /* XXX if (!READY) ??? */
227
228         do {
229                 dfui_tcp_be_ll_reply(c, fmsg);
230
231                 /*
232                  * Here, the frontend has picked up our request and is
233                  * processing it.  We have to wait for the response.
234                  */
235
236                 dfui_tcp_be_ll_receive(c);
237         
238                 /*
239                  * Did we get READY from this?
240                  * If so, loop!
241                  */
242
243         } while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY);
244
245         fmsg[0] = DFUI_BE_MSG_READY;
246         fmsg[1] = '\0';
247         dfui_tcp_be_ll_reply(c, fmsg);
248
249         free(fmsg);
250         return(DFUI_SUCCESS);
251 }
252
253 /*
254  * Receive a message from the frontend.
255  * This call is synchronous.
256  * After this call, the NUL-terminated message is available in
257  * c->ebuf.
258  */
259 dfui_err_t
260 dfui_tcp_be_ll_receive(struct dfui_connection *c)
261 {
262         int length;
263         char *buf;
264
265         top:
266
267         if (!T_TCP(c)->is_connected) {
268         dfui_debug("NOT_CONNECTED,ACCEPTING_ON<<%d>>\n", T_TCP(c)->listen_sd);
269                 T_TCP(c)->connected_sd = accept(T_TCP(c)->listen_sd, NULL, NULL);
270                 dfui_debug("ACCEPTED<<%d>>\n", T_TCP(c)->connected_sd);
271                 T_TCP(c)->stream = fdopen(T_TCP(c)->connected_sd, "r+");
272                 T_TCP(c)->is_connected = 1;
273         } else {
274                 dfui_debug("ALREADY_CONNECTED<<>>\n");
275         }
276
277         dfui_debug("WAITING<<>>\n");
278
279         if (read_data(T_TCP(c)->stream, (char *)&length, sizeof(length)) == -1) {
280                 dfui_debug("LOST_THEM<<>>\n");
281                 fclose(T_TCP(c)->stream);
282                 T_TCP(c)->is_connected = 0;
283                 goto top;
284         }
285
286         buf = malloc(length + 1);
287         if (read_data(T_TCP(c)->stream, buf, length) == -1) {
288                 dfui_debug("LOST_THEM<<>>\n");
289                 fclose(T_TCP(c)->stream);
290                 T_TCP(c)->is_connected = 0;
291                 goto top;
292         }
293
294         aura_buffer_set(c->ebuf, buf, length);
295         free(buf);
296
297         dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c->ebuf));
298
299         return(DFUI_SUCCESS);
300 }
301
302 /*
303  * Send a NUL-terminated reply to the frontend.
304  */
305 dfui_err_t
306 dfui_tcp_be_ll_reply(struct dfui_connection *c, const char *fmsg)
307 {
308         int length;
309
310         dfui_debug("SEND<<%s>>\n", fmsg);
311         length = strlen(fmsg);
312         write_data(T_TCP(c)->stream, (char *)&length, sizeof(length));
313         write_data(T_TCP(c)->stream, fmsg, length);
314
315         return(DFUI_SUCCESS);
316 }
317
318 /******** FRONTEND ********/
319
320 /** High Level **/
321
322 dfui_err_t
323 dfui_tcp_fe_connect(struct dfui_connection *c)
324 {
325         struct sockaddr_in servaddr;
326         int server_port;
327         int connected = 0;
328
329         server_port = atoi(c->rendezvous);
330
331         /*
332          * Create the tcp socket
333          */
334         while (!connected) {
335                 errno = 0;
336                 if ((T_TCP(c)->connected_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
337                         return(DFUI_FAILURE);
338                 }
339
340                 dfui_debug("CLIENT_SOCKET<<%d>>\n", T_TCP(c)->connected_sd);
341                 bzero(&servaddr, sizeof(servaddr));
342                 servaddr.sin_family = AF_INET;
343                 servaddr.sin_port = htons(server_port);
344                 inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
345
346                 if (connect(T_TCP(c)->connected_sd, (struct sockaddr *)&servaddr,
347                     sizeof(servaddr)) == 0) {
348                         dfui_debug("CONNECTED<<>>\n");
349                         connected = 1;
350                 } else {
351                         dfui_debug("NO_CONNECT<<>>\n");
352                         close(T_TCP(c)->connected_sd);
353                         sleep(1);
354                 }
355         }
356
357         /* at this point we should be connected */
358
359         T_TCP(c)->stream = fdopen(T_TCP(c)->connected_sd, "r+");
360
361         return(DFUI_SUCCESS);
362 }
363
364 dfui_err_t
365 dfui_tcp_fe_disconnect(struct dfui_connection *c)
366 {
367         close(T_TCP(c)->connected_sd);
368         return(DFUI_SUCCESS);
369 }
370
371 /** Low Level **/
372
373 /*
374  * Ask for, and subsequently receieve, a message from the backend.
375  * msgtype should be one of the DFUI_FE_MSG_* constants.
376  * This call is synchronous.
377  * After this call, the null-terminated, encoded message is
378  * available in T_TCP(c)->buf.
379  */
380 dfui_err_t
381 dfui_tcp_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg)
382 {
383         char *fmsg, *buf;
384         int length, result;
385
386         /*
387          * First, assert that the connection is open.
388          */
389         
390         if (c == NULL || T_TCP(c)->connected_sd == -1)
391                 return(DFUI_FAILURE);
392
393         /*
394          * Construct a message.
395          */
396
397         fmsg = malloc(strlen(msg) + 2);
398         fmsg[0] = msgtype;
399         strcpy(fmsg + 1, msg);
400         dfui_debug("SEND<<%s>>\n", fmsg);
401
402         /*
403          * Send a NUL-terminated message to the backend.
404          */
405
406         length = strlen(fmsg);
407         result = write_data(T_TCP(c)->stream, (char *)&length, sizeof(length));
408         dfui_debug("result<<%d>>\n", result);
409         result = write_data(T_TCP(c)->stream, (char *)fmsg, length);
410         dfui_debug("result<<%d>>\n", result);
411
412         /*
413          * Receive a reply from the backend.
414          * If our message was a READY, this should be a message like PRESENT.
415          * Otherwise it should simply be a READY.
416          */
417
418         dfui_debug("WAITING<<>>\n");
419         result = read_data(T_TCP(c)->stream, (char *)&length, sizeof(length));
420         dfui_debug("result<<%d>>\n", result);
421         buf = malloc(length + 1);
422         result = read_data(T_TCP(c)->stream, buf, length);
423         dfui_debug("result<<%d>>\n", result);
424         aura_buffer_set(c->ebuf, buf, length);
425         free(buf);
426
427         dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf));
428
429         free(fmsg);
430
431         return(DFUI_SUCCESS);
432 }
433
434 int
435 read_data(FILE *f, char *buf, int n)
436 {
437         int bcount;     /* counts bytes read */ 
438         int br;         /* bytes read this pass */ 
439
440         bcount = 0;
441         br = 0;
442         while (bcount < n) {
443                 if ((br = fread(buf, 1, n - bcount, f)) > 0) {
444                         dfui_debug("READ_BYTES<<%d>>\n", br);
445                         bcount += br;
446                         buf += br;
447                 } else if (br <= 0) {
448                         dfui_debug("read_data_error<<%d>>\n", br);
449                         return(-1);
450                 }
451         }
452         return(bcount);
453 }
454
455 int
456 write_data(FILE *f, const char *buf, int n)
457 {
458         int bcount;     /* counts bytes written */
459         int bw;         /* bytes written this pass */
460
461         bcount = 0;
462         bw = 0;
463         while (bcount < n) {
464                 if ((bw = fwrite(buf, 1, n - bcount, f)) > 0) {
465                         dfui_debug("WROTE_BYTES<<%d>>\n", bw);
466                         bcount += bw;
467                         buf += bw;
468                 } else if (bw <= 0) {
469                         dfui_debug("write_data_error<<%d>>\n", bw);
470                         return(-1);
471                 }
472         }
473         return(bcount);
474 }
475
476 #endif /* HAS_TCP */