3 * Copyright (c) 2004 Scott Ullrich <GeekGod@GeekGod.com>
4 * Portions Copyright (c) 2004 Chris Pressey <cpressey@catseye.mine.nu>
6 * Copyright (c) 2004 The DragonFly Project.
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).
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
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
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.
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
52 * $Id: conn_tcp.c,v 1.16 2005/02/06 19:53:19 cpressey Exp $
58 #include <sys/types.h>
61 #include <sys/errno.h>
62 #include <sys/socket.h>
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
73 #include <libaura/buffer.h>
75 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS
77 #undef NEEDS_DFUI_STRUCTURE_DEFINITIONS
82 /***** BACKEND ******/
87 * Connect to the frontend.
90 dfui_tcp_be_start(struct dfui_connection
*c
)
92 struct sockaddr_in servaddr
;
96 server_port
= atoi(c
->rendezvous
);
99 * Create the tcp socket
102 if ((T_TCP(c
)->listen_sd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1)
103 return(DFUI_FAILURE
);
104 dfui_debug("LISTEN_SOCKET<<%d>>\n", T_TCP(c
)->listen_sd
);
106 if (setsockopt(T_TCP(c
)->listen_sd
, SOL_SOCKET
, SO_REUSEADDR
,
107 &tru
, sizeof(tru
)) == -1) {
108 return(DFUI_FAILURE
);
111 bzero(&servaddr
, sizeof(servaddr
));
112 servaddr
.sin_family
= AF_INET
;
113 servaddr
.sin_port
= htons(server_port
);
114 switch(inet_pton(AF_INET
, "127.0.0.1", &servaddr
.sin_addr
)) {
116 warnx("inet_pton(): address not parseable");
117 return(DFUI_FAILURE
);
122 return(DFUI_FAILURE
);
125 if (bind(T_TCP(c
)->listen_sd
, (struct sockaddr
*)&servaddr
, sizeof(servaddr
)) == -1) {
127 return(DFUI_FAILURE
);
129 dfui_debug("BOUND_ON<<%d>>\n", T_TCP(c
)->listen_sd
);
130 if (listen(T_TCP(c
)->listen_sd
, 0) == -1)
131 return(DFUI_FAILURE
);
132 dfui_debug("LISTENING_ON<<%d>>\n", T_TCP(c
)->listen_sd
);
133 /* at this point we should be listening on the rendezvous port */
134 return(DFUI_SUCCESS
);
138 * Tell the frontend that we're done and disconnect from it.
141 dfui_tcp_be_stop(struct dfui_connection
*c
)
143 if (dfui_tcp_be_ll_exchange(c
, DFUI_BE_MSG_STOP
, "")) {
144 close(T_TCP(c
)->listen_sd
);
145 close(T_TCP(c
)->connected_sd
);
146 fclose(T_TCP(c
)->stream
);
147 return(DFUI_SUCCESS
);
149 return(DFUI_FAILURE
);
155 * Exchange a message with the frontend. This involves two receive()/reply()
156 * cycles: one to provide our message, one to get a reply from the frontend.
158 * Note that this does not immediately send the message to the frontend -
159 * it can't, because we're a service and it's a client. What it does is
160 * keep the message handy and wait for a frontend request to come in. It
161 * then replies to that request with our message.
163 * The protocol looks something like the following, using the PRESENT and
164 * SUBMIT exchange as an example:
166 * frontend (client) | backend (service)
167 * ------------------+------------------
170 * READY --> ll_receive()
171 * <-- PRESENT(form) ll_reply()
174 * SUBMIT(form) --> ll_receive()
175 * <-- READY ll_reply()
177 * Each of those exchanges is a pair of calls, on our end, to
178 * dfui_tcp_be_ll_receive() and dfui_npipe_be_ll_reply().
180 * The set of messages that the client can pass us is determined by
181 * the conversation state:
183 * o In stage 1, only READY and ABORT are meaningful.
184 * o After a PRESENT, the messages SUBMIT and ABORT are meaningul
186 * o During a PROG_*, the messages CONTINUE, CANCEL, and ABORT
187 * are meaningful in stage 2.
189 * If the frontend sends us with READY in stage 2, we assume it has
190 * fallen out of sync, so we send the same initial reply again, going
191 * back to stage 1 as it were.
193 * After this call, the message is available in c->ebuf.
196 dfui_tcp_be_ll_exchange(struct dfui_connection
*c
, char msgtype
, const char *msg
)
201 * Construct our message to send.
204 fmsg
= malloc(strlen(msg
) + 2);
206 strcpy(fmsg
+ 1, msg
);
209 * Get the frontend's message.
212 dfui_tcp_be_ll_receive(c
);
215 * Frontend message should have been either READY or ABORT.
216 * If ABORT, we get out of here pronto.
219 if (aura_buffer_buf(c
->ebuf
)[0] == DFUI_FE_MSG_ABORT
) {
221 return(DFUI_FAILURE
);
224 /* XXX if (!READY) ??? */
227 dfui_tcp_be_ll_reply(c
, fmsg
);
230 * Here, the frontend has picked up our request and is
231 * processing it. We have to wait for the response.
234 dfui_tcp_be_ll_receive(c
);
237 * Did we get READY from this?
241 } while (aura_buffer_buf(c
->ebuf
)[0] == DFUI_FE_MSG_READY
);
243 fmsg
[0] = DFUI_BE_MSG_READY
;
245 dfui_tcp_be_ll_reply(c
, fmsg
);
248 return(DFUI_SUCCESS
);
252 * Receive a message from the frontend.
253 * This call is synchronous.
254 * After this call, the NUL-terminated message is available in
258 dfui_tcp_be_ll_receive(struct dfui_connection
*c
)
265 if (!T_TCP(c
)->is_connected
) {
266 dfui_debug("NOT_CONNECTED,ACCEPTING_ON<<%d>>\n", T_TCP(c
)->listen_sd
);
267 T_TCP(c
)->connected_sd
= accept(T_TCP(c
)->listen_sd
, NULL
, NULL
);
268 dfui_debug("ACCEPTED<<%d>>\n", T_TCP(c
)->connected_sd
);
269 T_TCP(c
)->stream
= fdopen(T_TCP(c
)->connected_sd
, "r+");
270 T_TCP(c
)->is_connected
= 1;
272 dfui_debug("ALREADY_CONNECTED<<>>\n");
275 dfui_debug("WAITING<<>>\n");
277 if (read_data(T_TCP(c
)->stream
, (char *)&length
, sizeof(length
)) == -1) {
278 dfui_debug("LOST_THEM<<>>\n");
279 fclose(T_TCP(c
)->stream
);
280 T_TCP(c
)->is_connected
= 0;
284 buf
= malloc(length
+ 1);
285 if (read_data(T_TCP(c
)->stream
, buf
, length
) == -1) {
286 dfui_debug("LOST_THEM<<>>\n");
287 fclose(T_TCP(c
)->stream
);
288 T_TCP(c
)->is_connected
= 0;
292 aura_buffer_set(c
->ebuf
, buf
, length
);
295 dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c
->ebuf
));
297 return(DFUI_SUCCESS
);
301 * Send a NUL-terminated reply to the frontend.
304 dfui_tcp_be_ll_reply(struct dfui_connection
*c
, const char *fmsg
)
308 dfui_debug("SEND<<%s>>\n", fmsg
);
309 length
= strlen(fmsg
);
310 write_data(T_TCP(c
)->stream
, (char *)&length
, sizeof(length
));
311 write_data(T_TCP(c
)->stream
, fmsg
, length
);
313 return(DFUI_SUCCESS
);
316 /******** FRONTEND ********/
321 dfui_tcp_fe_connect(struct dfui_connection
*c
)
323 struct sockaddr_in servaddr
;
327 server_port
= atoi(c
->rendezvous
);
330 * Create the tcp socket
334 if ((T_TCP(c
)->connected_sd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
335 return(DFUI_FAILURE
);
338 dfui_debug("CLIENT_SOCKET<<%d>>\n", T_TCP(c
)->connected_sd
);
339 bzero(&servaddr
, sizeof(servaddr
));
340 servaddr
.sin_family
= AF_INET
;
341 servaddr
.sin_port
= htons(server_port
);
342 inet_pton(AF_INET
, "127.0.0.1", &servaddr
.sin_addr
);
344 if (connect(T_TCP(c
)->connected_sd
, (struct sockaddr
*)&servaddr
,
345 sizeof(servaddr
)) == 0) {
346 dfui_debug("CONNECTED<<>>\n");
349 dfui_debug("NO_CONNECT<<>>\n");
350 close(T_TCP(c
)->connected_sd
);
355 /* at this point we should be connected */
357 T_TCP(c
)->stream
= fdopen(T_TCP(c
)->connected_sd
, "r+");
359 return(DFUI_SUCCESS
);
363 dfui_tcp_fe_disconnect(struct dfui_connection
*c
)
365 close(T_TCP(c
)->connected_sd
);
366 return(DFUI_SUCCESS
);
372 * Ask for, and subsequently receieve, a message from the backend.
373 * msgtype should be one of the DFUI_FE_MSG_* constants.
374 * This call is synchronous.
375 * After this call, the null-terminated, encoded message is
376 * available in T_TCP(c)->buf.
379 dfui_tcp_fe_ll_request(struct dfui_connection
*c
, char msgtype
, const char *msg
)
385 * First, assert that the connection is open.
388 if (c
== NULL
|| T_TCP(c
)->connected_sd
== -1)
389 return(DFUI_FAILURE
);
392 * Construct a message.
395 fmsg
= malloc(strlen(msg
) + 2);
397 strcpy(fmsg
+ 1, msg
);
398 dfui_debug("SEND<<%s>>\n", fmsg
);
401 * Send a NUL-terminated message to the backend.
404 length
= strlen(fmsg
);
405 result
= write_data(T_TCP(c
)->stream
, (char *)&length
, sizeof(length
));
406 dfui_debug("result<<%d>>\n", result
);
407 result
= write_data(T_TCP(c
)->stream
, (char *)fmsg
, length
);
408 dfui_debug("result<<%d>>\n", result
);
411 * Receive a reply from the backend.
412 * If our message was a READY, this should be a message like PRESENT.
413 * Otherwise it should simply be a READY.
416 dfui_debug("WAITING<<>>\n");
417 result
= read_data(T_TCP(c
)->stream
, (char *)&length
, sizeof(length
));
418 dfui_debug("result<<%d>>\n", result
);
419 buf
= malloc(length
+ 1);
420 result
= read_data(T_TCP(c
)->stream
, buf
, length
);
421 dfui_debug("result<<%d>>\n", result
);
422 aura_buffer_set(c
->ebuf
, buf
, length
);
425 dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c
->ebuf
));
429 return(DFUI_SUCCESS
);
433 read_data(FILE *f
, char *buf
, int n
)
435 int bcount
; /* counts bytes read */
436 int br
; /* bytes read this pass */
441 if ((br
= fread(buf
, 1, n
- bcount
, f
)) > 0) {
442 dfui_debug("READ_BYTES<<%d>>\n", br
);
445 } else if (br
<= 0) {
446 dfui_debug("read_data_error<<%d>>\n", br
);
454 write_data(FILE *f
, const char *buf
, int n
)
456 int bcount
; /* counts bytes written */
457 int bw
; /* bytes written this pass */
462 if ((bw
= fwrite(buf
, 1, n
- bcount
, f
)) > 0) {
463 dfui_debug("WROTE_BYTES<<%d>>\n", bw
);
466 } else if (bw
<= 0) {
467 dfui_debug("write_data_error<<%d>>\n", bw
);