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