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/socket.h>
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
65 
66 #include <err.h>
67 #include <errno.h>
68 #include <stdarg.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 
74 #include <libaura/buffer.h>
75 
76 #define	NEEDS_DFUI_STRUCTURE_DEFINITIONS
77 #include "dfui.h"
78 #undef	NEEDS_DFUI_STRUCTURE_DEFINITIONS
79 #include "encoding.h"
80 #include "conn_tcp.h"
81 #include "dump.h"
82 
83 /***** BACKEND ******/
84 
85 /** High Level **/
86 
87 /*
88  * Connect to the frontend.
89  */
90 dfui_err_t
91 dfui_tcp_be_start(struct dfui_connection *c)
92 {
93 	struct sockaddr_in servaddr;
94 	int server_port;
95 	int tru = 1;
96 
97 	server_port = atoi(c->rendezvous);
98 
99 	/*
100 	 * Create the tcp socket
101 	 */
102 	errno = 0;
103 	if ((T_TCP(c)->listen_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
104 		return(DFUI_FAILURE);
105 	dfui_debug("LISTEN_SOCKET<<%d>>\n", T_TCP(c)->listen_sd);
106 
107 	if (setsockopt(T_TCP(c)->listen_sd, SOL_SOCKET, SO_REUSEADDR,
108 	    &tru, sizeof(tru)) == -1) {
109 		return(DFUI_FAILURE);
110 	}
111 
112 	bzero(&servaddr, sizeof(servaddr));
113 	servaddr.sin_family = AF_INET;
114 	servaddr.sin_port = htons(server_port);
115 	switch(inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr)) {
116 	case 0:
117 		warnx("inet_pton(): address not parseable");
118 		return(DFUI_FAILURE);
119 	case 1:
120 		break;
121 	default:
122 		warn("inet_pton()");
123 		return(DFUI_FAILURE);
124 	}
125 
126 	if (bind(T_TCP(c)->listen_sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
127 		warn("bind()");
128 		return(DFUI_FAILURE);
129 	}
130 	dfui_debug("BOUND_ON<<%d>>\n", T_TCP(c)->listen_sd);
131 	if (listen(T_TCP(c)->listen_sd, 0) == -1)
132 		return(DFUI_FAILURE);
133 	dfui_debug("LISTENING_ON<<%d>>\n", T_TCP(c)->listen_sd);
134 	/* at this point we should be listening on the rendezvous port */
135 	return(DFUI_SUCCESS);
136 }
137 
138 /*
139  * Tell the frontend that we're done and disconnect from it.
140  */
141 dfui_err_t
142 dfui_tcp_be_stop(struct dfui_connection *c)
143 {
144 	if (dfui_tcp_be_ll_exchange(c, DFUI_BE_MSG_STOP, "")) {
145 		close(T_TCP(c)->listen_sd);
146 		close(T_TCP(c)->connected_sd);
147 		fclose(T_TCP(c)->stream);
148 		return(DFUI_SUCCESS);
149 	} else
150 		return(DFUI_FAILURE);
151 }
152 
153 /** Low Level **/
154 
155 /*
156  * Exchange a message with the frontend.  This involves two receive()/reply()
157  * cycles: one to provide our message, one to get a reply from the frontend.
158  *
159  * Note that this does not immediately send the message to the frontend -
160  * it can't, because we're a service and it's a client.  What it does is
161  * keep the message handy and wait for a frontend request to come in.  It
162  * then replies to that request with our message.
163  *
164  * The protocol looks something like the following, using the PRESENT and
165  * SUBMIT exchange as an example:
166  *
167  * frontend (client) | backend (service)
168  * ------------------+------------------
169  *
170  *                                     [stage 1]
171  * READY            -->                ll_receive()
172  *                 <--  PRESENT(form)  ll_reply()
173  *
174  *                                     [stage 2]
175  * SUBMIT(form)     -->                ll_receive()
176  *                 <--  READY          ll_reply()
177  *
178  * Each of those exchanges is a pair of calls, on our end, to
179  * dfui_tcp_be_ll_receive() and dfui_npipe_be_ll_reply().
180  *
181  * The set of messages that the client can pass us is determined by
182  * the conversation state:
183  *
184  *   o  In stage 1, only READY and ABORT are meaningful.
185  *   o  After a PRESENT, the messages SUBMIT and ABORT are meaningul
186  *      in stage 2.
187  *   o  During a PROG_*, the messages CONTINUE, CANCEL, and ABORT
188  *      are meaningful in stage 2.
189  *
190  * If the frontend sends us with READY in stage 2, we assume it has
191  * fallen out of sync, so we send the same initial reply again, going
192  * back to stage 1 as it were.
193  *
194  * After this call, the message is available in c->ebuf.
195  */
196 dfui_err_t
197 dfui_tcp_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg)
198 {
199 	char *fmsg;
200 
201 	/*
202 	 * Construct our message to send.
203 	 */
204 
205 	fmsg = malloc(strlen(msg) + 2);
206 	fmsg[0] = msgtype;
207 	strcpy(fmsg + 1, msg);
208 
209 	/*
210 	 * Get the frontend's message.
211 	 */
212 
213 	dfui_tcp_be_ll_receive(c);
214 
215 	/*
216 	 * Frontend message should have been either READY or ABORT.
217 	 * If ABORT, we get out of here pronto.
218 	 */
219 
220 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
221 		free(fmsg);
222 		return(DFUI_FAILURE);
223 	}
224 
225 	/* XXX if (!READY) ??? */
226 
227 	do {
228 		dfui_tcp_be_ll_reply(c, fmsg);
229 
230 		/*
231 		 * Here, the frontend has picked up our request and is
232 		 * processing it.  We have to wait for the response.
233 		 */
234 
235 		dfui_tcp_be_ll_receive(c);
236 
237 		/*
238 		 * Did we get READY from this?
239 		 * If so, loop!
240 		 */
241 
242 	} while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY);
243 
244 	fmsg[0] = DFUI_BE_MSG_READY;
245 	fmsg[1] = '\0';
246 	dfui_tcp_be_ll_reply(c, fmsg);
247 
248 	free(fmsg);
249 	return(DFUI_SUCCESS);
250 }
251 
252 /*
253  * Receive a message from the frontend.
254  * This call is synchronous.
255  * After this call, the NUL-terminated message is available in
256  * c->ebuf.
257  */
258 dfui_err_t
259 dfui_tcp_be_ll_receive(struct dfui_connection *c)
260 {
261 	int length;
262 	char *buf;
263 
264 	top:
265 
266 	if (!T_TCP(c)->is_connected) {
267 	dfui_debug("NOT_CONNECTED,ACCEPTING_ON<<%d>>\n", T_TCP(c)->listen_sd);
268 		T_TCP(c)->connected_sd = accept(T_TCP(c)->listen_sd, NULL, NULL);
269 		dfui_debug("ACCEPTED<<%d>>\n", T_TCP(c)->connected_sd);
270 		T_TCP(c)->stream = fdopen(T_TCP(c)->connected_sd, "r+");
271 		T_TCP(c)->is_connected = 1;
272 	} else {
273 		dfui_debug("ALREADY_CONNECTED<<>>\n");
274 	}
275 
276 	dfui_debug("WAITING<<>>\n");
277 
278 	if (read_data(T_TCP(c)->stream, (char *)&length, sizeof(length)) == -1) {
279 		dfui_debug("LOST_THEM<<>>\n");
280 		fclose(T_TCP(c)->stream);
281 		T_TCP(c)->is_connected = 0;
282 		goto top;
283 	}
284 
285 	buf = malloc(length + 1);
286 	if (read_data(T_TCP(c)->stream, buf, length) == -1) {
287 		dfui_debug("LOST_THEM<<>>\n");
288 		fclose(T_TCP(c)->stream);
289 		T_TCP(c)->is_connected = 0;
290 		goto top;
291 	}
292 
293 	aura_buffer_set(c->ebuf, buf, length);
294 	free(buf);
295 
296 	dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c->ebuf));
297 
298 	return(DFUI_SUCCESS);
299 }
300 
301 /*
302  * Send a NUL-terminated reply to the frontend.
303  */
304 dfui_err_t
305 dfui_tcp_be_ll_reply(struct dfui_connection *c, const char *fmsg)
306 {
307 	int length;
308 
309 	dfui_debug("SEND<<%s>>\n", fmsg);
310 	length = strlen(fmsg);
311 	write_data(T_TCP(c)->stream, (char *)&length, sizeof(length));
312 	write_data(T_TCP(c)->stream, fmsg, length);
313 
314 	return(DFUI_SUCCESS);
315 }
316 
317 /******** FRONTEND ********/
318 
319 /** High Level **/
320 
321 dfui_err_t
322 dfui_tcp_fe_connect(struct dfui_connection *c)
323 {
324         struct sockaddr_in servaddr;
325         int server_port;
326 	int connected = 0;
327 
328         server_port = atoi(c->rendezvous);
329 
330         /*
331          * Create the tcp socket
332          */
333 	while (!connected) {
334 		errno = 0;
335 		if ((T_TCP(c)->connected_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
336 			return(DFUI_FAILURE);
337 		}
338 
339 		dfui_debug("CLIENT_SOCKET<<%d>>\n", T_TCP(c)->connected_sd);
340 		bzero(&servaddr, sizeof(servaddr));
341 		servaddr.sin_family = AF_INET;
342 		servaddr.sin_port = htons(server_port);
343 		inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
344 
345 		if (connect(T_TCP(c)->connected_sd, (struct sockaddr *)&servaddr,
346 		    sizeof(servaddr)) == 0) {
347 			dfui_debug("CONNECTED<<>>\n");
348 			connected = 1;
349 		} else {
350 			dfui_debug("NO_CONNECT<<>>\n");
351 			close(T_TCP(c)->connected_sd);
352 			sleep(1);
353 		}
354 	}
355 
356         /* at this point we should be connected */
357 
358 	T_TCP(c)->stream = fdopen(T_TCP(c)->connected_sd, "r+");
359 
360         return(DFUI_SUCCESS);
361 }
362 
363 dfui_err_t
364 dfui_tcp_fe_disconnect(struct dfui_connection *c)
365 {
366 	close(T_TCP(c)->connected_sd);
367 	return(DFUI_SUCCESS);
368 }
369 
370 /** Low Level **/
371 
372 /*
373  * Ask for, and subsequently receieve, a message from the backend.
374  * msgtype should be one of the DFUI_FE_MSG_* constants.
375  * This call is synchronous.
376  * After this call, the null-terminated, encoded message is
377  * available in T_TCP(c)->buf.
378  */
379 dfui_err_t
380 dfui_tcp_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg)
381 {
382 	char *fmsg, *buf;
383 	int length, result;
384 
385 	/*
386 	 * First, assert that the connection is open.
387 	 */
388 
389 	if (c == NULL || T_TCP(c)->connected_sd == -1)
390 		return(DFUI_FAILURE);
391 
392 	/*
393 	 * Construct a message.
394 	 */
395 
396 	fmsg = malloc(strlen(msg) + 2);
397 	fmsg[0] = msgtype;
398 	strcpy(fmsg + 1, msg);
399 	dfui_debug("SEND<<%s>>\n", fmsg);
400 
401 	/*
402 	 * Send a NUL-terminated message to the backend.
403 	 */
404 
405         length = strlen(fmsg);
406         result = write_data(T_TCP(c)->stream, (char *)&length, sizeof(length));
407 	dfui_debug("result<<%d>>\n", result);
408 	result = write_data(T_TCP(c)->stream, (char *)fmsg, length);
409 	dfui_debug("result<<%d>>\n", result);
410 
411 	/*
412 	 * Receive a reply from the backend.
413 	 * If our message was a READY, this should be a message like PRESENT.
414 	 * Otherwise it should simply be a READY.
415 	 */
416 
417 	dfui_debug("WAITING<<>>\n");
418         result = read_data(T_TCP(c)->stream, (char *)&length, sizeof(length));
419 	dfui_debug("result<<%d>>\n", result);
420         buf = malloc(length + 1);
421         result = read_data(T_TCP(c)->stream, buf, length);
422 	dfui_debug("result<<%d>>\n", result);
423         aura_buffer_set(c->ebuf, buf, length);
424         free(buf);
425 
426 	dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf));
427 
428 	free(fmsg);
429 
430 	return(DFUI_SUCCESS);
431 }
432 
433 int
434 read_data(FILE *f, char *buf, int n)
435 {
436 	int bcount;	/* counts bytes read */
437 	int br;		/* bytes read this pass */
438 
439 	bcount = 0;
440 	br = 0;
441 	while (bcount < n) {
442 		if ((br = fread(buf, 1, n - bcount, f)) > 0) {
443 			dfui_debug("READ_BYTES<<%d>>\n", br);
444 			bcount += br;
445 			buf += br;
446 		} else if (br <= 0) {
447 			dfui_debug("read_data_error<<%d>>\n", br);
448 			return(-1);
449 		}
450 	}
451 	return(bcount);
452 }
453 
454 int
455 write_data(FILE *f, const char *buf, int n)
456 {
457         int bcount;	/* counts bytes written */
458         int bw;		/* bytes written this pass */
459 
460         bcount = 0;
461         bw = 0;
462         while (bcount < n) {
463                 if ((bw = fwrite(buf, 1, n - bcount, f)) > 0) {
464 			dfui_debug("WROTE_BYTES<<%d>>\n", bw);
465                         bcount += bw;
466                         buf += bw;
467                 } else if (bw <= 0) {
468 			dfui_debug("write_data_error<<%d>>\n", bw);
469 			return(-1);
470 		}
471         }
472         return(bcount);
473 }
474 
475 #endif /* HAS_TCP */
476