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 <stdarg.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <unistd.h> 72 73 #include <libaura/buffer.h> 74 75 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS 76 #include "dfui.h" 77 #undef NEEDS_DFUI_STRUCTURE_DEFINITIONS 78 #include "encoding.h" 79 #include "conn_tcp.h" 80 #include "dump.h" 81 82 /***** BACKEND ******/ 83 84 /** High Level **/ 85 86 /* 87 * Connect to the frontend. 88 */ 89 dfui_err_t 90 dfui_tcp_be_start(struct dfui_connection *c) 91 { 92 struct sockaddr_in servaddr; 93 int server_port; 94 int tru = 1; 95 96 server_port = atoi(c->rendezvous); 97 98 /* 99 * Create the tcp socket 100 */ 101 errno = 0; 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); 105 106 if (setsockopt(T_TCP(c)->listen_sd, SOL_SOCKET, SO_REUSEADDR, 107 &tru, sizeof(tru)) == -1) { 108 return(DFUI_FAILURE); 109 } 110 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)) { 115 case 0: 116 warnx("inet_pton(): address not parseable"); 117 return(DFUI_FAILURE); 118 case 1: 119 break; 120 default: 121 warn("inet_pton()"); 122 return(DFUI_FAILURE); 123 } 124 125 if (bind(T_TCP(c)->listen_sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { 126 warn("bind()"); 127 return(DFUI_FAILURE); 128 } 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); 135 } 136 137 /* 138 * Tell the frontend that we're done and disconnect from it. 139 */ 140 dfui_err_t 141 dfui_tcp_be_stop(struct dfui_connection *c) 142 { 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); 148 } else 149 return(DFUI_FAILURE); 150 } 151 152 /** Low Level **/ 153 154 /* 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. 157 * 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. 162 * 163 * The protocol looks something like the following, using the PRESENT and 164 * SUBMIT exchange as an example: 165 * 166 * frontend (client) | backend (service) 167 * ------------------+------------------ 168 * 169 * [stage 1] 170 * READY --> ll_receive() 171 * <-- PRESENT(form) ll_reply() 172 * 173 * [stage 2] 174 * SUBMIT(form) --> ll_receive() 175 * <-- READY ll_reply() 176 * 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(). 179 * 180 * The set of messages that the client can pass us is determined by 181 * the conversation state: 182 * 183 * o In stage 1, only READY and ABORT are meaningful. 184 * o After a PRESENT, the messages SUBMIT and ABORT are meaningul 185 * in stage 2. 186 * o During a PROG_*, the messages CONTINUE, CANCEL, and ABORT 187 * are meaningful in stage 2. 188 * 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. 192 * 193 * After this call, the message is available in c->ebuf. 194 */ 195 dfui_err_t 196 dfui_tcp_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg) 197 { 198 char *fmsg; 199 200 /* 201 * Construct our message to send. 202 */ 203 204 fmsg = malloc(strlen(msg) + 2); 205 fmsg[0] = msgtype; 206 strcpy(fmsg + 1, msg); 207 208 /* 209 * Get the frontend's message. 210 */ 211 212 dfui_tcp_be_ll_receive(c); 213 214 /* 215 * Frontend message should have been either READY or ABORT. 216 * If ABORT, we get out of here pronto. 217 */ 218 219 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) { 220 free(fmsg); 221 return(DFUI_FAILURE); 222 } 223 224 /* XXX if (!READY) ??? */ 225 226 do { 227 dfui_tcp_be_ll_reply(c, fmsg); 228 229 /* 230 * Here, the frontend has picked up our request and is 231 * processing it. We have to wait for the response. 232 */ 233 234 dfui_tcp_be_ll_receive(c); 235 236 /* 237 * Did we get READY from this? 238 * If so, loop! 239 */ 240 241 } while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY); 242 243 fmsg[0] = DFUI_BE_MSG_READY; 244 fmsg[1] = '\0'; 245 dfui_tcp_be_ll_reply(c, fmsg); 246 247 free(fmsg); 248 return(DFUI_SUCCESS); 249 } 250 251 /* 252 * Receive a message from the frontend. 253 * This call is synchronous. 254 * After this call, the NUL-terminated message is available in 255 * c->ebuf. 256 */ 257 dfui_err_t 258 dfui_tcp_be_ll_receive(struct dfui_connection *c) 259 { 260 int length; 261 char *buf; 262 263 top: 264 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; 271 } else { 272 dfui_debug("ALREADY_CONNECTED<<>>\n"); 273 } 274 275 dfui_debug("WAITING<<>>\n"); 276 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; 281 goto top; 282 } 283 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; 289 goto top; 290 } 291 292 aura_buffer_set(c->ebuf, buf, length); 293 free(buf); 294 295 dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c->ebuf)); 296 297 return(DFUI_SUCCESS); 298 } 299 300 /* 301 * Send a NUL-terminated reply to the frontend. 302 */ 303 dfui_err_t 304 dfui_tcp_be_ll_reply(struct dfui_connection *c, const char *fmsg) 305 { 306 int length; 307 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); 312 313 return(DFUI_SUCCESS); 314 } 315 316 /******** FRONTEND ********/ 317 318 /** High Level **/ 319 320 dfui_err_t 321 dfui_tcp_fe_connect(struct dfui_connection *c) 322 { 323 struct sockaddr_in servaddr; 324 int server_port; 325 int connected = 0; 326 327 server_port = atoi(c->rendezvous); 328 329 /* 330 * Create the tcp socket 331 */ 332 while (!connected) { 333 errno = 0; 334 if ((T_TCP(c)->connected_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 335 return(DFUI_FAILURE); 336 } 337 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); 343 344 if (connect(T_TCP(c)->connected_sd, (struct sockaddr *)&servaddr, 345 sizeof(servaddr)) == 0) { 346 dfui_debug("CONNECTED<<>>\n"); 347 connected = 1; 348 } else { 349 dfui_debug("NO_CONNECT<<>>\n"); 350 close(T_TCP(c)->connected_sd); 351 sleep(1); 352 } 353 } 354 355 /* at this point we should be connected */ 356 357 T_TCP(c)->stream = fdopen(T_TCP(c)->connected_sd, "r+"); 358 359 return(DFUI_SUCCESS); 360 } 361 362 dfui_err_t 363 dfui_tcp_fe_disconnect(struct dfui_connection *c) 364 { 365 close(T_TCP(c)->connected_sd); 366 return(DFUI_SUCCESS); 367 } 368 369 /** Low Level **/ 370 371 /* 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. 377 */ 378 dfui_err_t 379 dfui_tcp_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg) 380 { 381 char *fmsg, *buf; 382 int length, result; 383 384 /* 385 * First, assert that the connection is open. 386 */ 387 388 if (c == NULL || T_TCP(c)->connected_sd == -1) 389 return(DFUI_FAILURE); 390 391 /* 392 * Construct a message. 393 */ 394 395 fmsg = malloc(strlen(msg) + 2); 396 fmsg[0] = msgtype; 397 strcpy(fmsg + 1, msg); 398 dfui_debug("SEND<<%s>>\n", fmsg); 399 400 /* 401 * Send a NUL-terminated message to the backend. 402 */ 403 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); 409 410 /* 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. 414 */ 415 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); 423 free(buf); 424 425 dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf)); 426 427 free(fmsg); 428 429 return(DFUI_SUCCESS); 430 } 431 432 int 433 read_data(FILE *f, char *buf, int n) 434 { 435 int bcount; /* counts bytes read */ 436 int br; /* bytes read this pass */ 437 438 bcount = 0; 439 br = 0; 440 while (bcount < n) { 441 if ((br = fread(buf, 1, n - bcount, f)) > 0) { 442 dfui_debug("READ_BYTES<<%d>>\n", br); 443 bcount += br; 444 buf += br; 445 } else if (br <= 0) { 446 dfui_debug("read_data_error<<%d>>\n", br); 447 return(-1); 448 } 449 } 450 return(bcount); 451 } 452 453 int 454 write_data(FILE *f, const char *buf, int n) 455 { 456 int bcount; /* counts bytes written */ 457 int bw; /* bytes written this pass */ 458 459 bcount = 0; 460 bw = 0; 461 while (bcount < n) { 462 if ((bw = fwrite(buf, 1, n - bcount, f)) > 0) { 463 dfui_debug("WROTE_BYTES<<%d>>\n", bw); 464 bcount += bw; 465 buf += bw; 466 } else if (bw <= 0) { 467 dfui_debug("write_data_error<<%d>>\n", bw); 468 return(-1); 469 } 470 } 471 return(bcount); 472 } 473 474 #endif /* HAS_TCP */ 475