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 <libaura/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 */ 477