1/** \file 2 * \brief Simple program to send or receive files through a TCP/IP connection 3 */ 4 5/* 6 * Copyright (c) 2010, 2011, ETH Zurich. 7 * All rights reserved. 8 * 9 * This file is distributed under the terms in the attached LICENSE file. 10 * If you do not find this file, copies can be found by writing to: 11 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 12 */ 13 14#include <stdlib.h> 15#include <stdio.h> 16#include <string.h> 17#include <getopt.h> 18 19#include <errno.h> 20 21#include <barrelfish/barrelfish.h> 22#include <barrelfish/waitset.h> 23#include <barrelfish/nameservice_client.h> 24 25#include <lwip/netif.h> 26#include <lwip/dhcp.h> 27#include <netif/etharp.h> 28#include <lwip/init.h> 29#include <lwip/tcp.h> 30#include <lwip/ip_addr.h> 31#include <net/net.h> 32 33 34/* -------------- Coordination ----------------------*/ 35 36// condition used to singal controlling code to wait for a condition 37static bool wait_cond; 38 39static inline void 40wait_for_condition (void) { 41 while (wait_cond) { 42 messages_wait_and_handle_next(); 43 } 44} 45 46 47/* -------------- LWIP/network initialisation ----------------------*/ 48 49#define SEND_TIMER_MSECS 4 50 51 52/* -------------- Networking ----------------------*/ 53 54static err_t tcp_is_sent(void *arg, struct tcp_pcb *pcb, u16_t len); 55static err_t tcp_is_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, 56 err_t err); 57 58static void tcp_is_err(void *arg, err_t err) 59{ 60 debug_printf("tcp is err: %d\n", (int)err); 61 62} 63 64static err_t tcp_is_poll(void *arg, struct tcp_pcb *pcb) 65{ 66 debug_printf("tcp is poll\n"); 67 68 return ERR_OK; 69} 70 71 72 73static err_t tcp_is_connected(void *arg, struct tcp_pcb *pcb, err_t err) 74{ 75 // debug_printf("tcp connected\n"); 76 77 if (err != ERR_OK) { 78 fprintf(stderr, "tcp connection failed\n"); 79 wait_cond = false; 80 return err; 81 } 82 83 tcp_sent(pcb, tcp_is_sent); 84 tcp_recv(pcb, tcp_is_recv); 85 tcp_err( pcb, tcp_is_err); 86 tcp_poll(pcb, tcp_is_poll, 10); 87 88 wait_cond = false; 89 90 return ERR_OK; 91} 92 93 94static struct tcp_pcb *connect(ip_addr_t *ip, int port) 95{ 96 // debug_printf("connect()\n"); 97 98 err_t err; 99 struct tcp_pcb *pcb; 100 101 pcb = tcp_new(); 102 if (pcb == NULL) { 103 fprintf(stderr, "failed to create new pcb\n"); 104 assert(pcb != NULL); 105 return NULL; 106 } 107 108 // debug_printf("pcb created\n"); 109 110 111 // debug_printf("connecting to server\n"); 112 wait_cond = true; 113 err = tcp_connect(pcb, ip, port, tcp_is_connected); 114 wait_for_condition(); 115 116 // TODO: proper error handling 117 if (err != ERR_OK) { 118 fprintf(stderr, "error connecting %d\n", err); 119 return NULL; 120 } 121 122 return pcb; 123} 124 125 126static err_t tcp_server_accept(void *arg, struct tcp_pcb *tpcb, err_t err) 127{ 128 129 debug_printf("accepted new connection\n"); 130 131 tcp_setprio(tpcb, TCP_PRIO_MIN); 132 tcp_arg(tpcb, tpcb); 133 tcp_recv(tpcb, tcp_is_recv); 134 tcp_err(tpcb, tcp_is_err); 135 tcp_poll(tpcb, NULL, 4); 136 137 return ERR_OK; 138} 139 140 141static struct tcp_pcb *bind(int port) 142{ 143 // debug_printf("bind\n"); 144 struct tcp_pcb *pcb = tcp_new(); 145 if (pcb == NULL) { 146 return NULL; 147 } 148 149 // debug_printf("got new pcb\n"); 150 151 // debug_printf("calling tcp_bind\n"); 152 153 err_t err = tcp_bind(pcb, IP_ADDR_ANY, port); 154 if(err != ERR_OK) { 155 if (err == ERR_USE) { 156 fprintf(stderr, "Another connection is bound to the same port.\n"); 157 } 158 return NULL; 159 } 160 161 // debug_printf("tcp_bind completed\n"); 162 163 // debug_printf("calling tcp_listen\n"); 164 165 pcb = tcp_listen(pcb); 166 if (pcb == NULL) { 167 return NULL; 168 } 169 170 // debug_printf("finished tcp_listen\n"); 171 172 tcp_arg(pcb, pcb); //callback argument 173 tcp_accept(pcb, tcp_server_accept); 174 175 // debug_printf("bind finished\n"); 176 177 return pcb; 178} 179 180static void close_connection(struct tcp_pcb *pcb) 181{ 182 //wait_cond = true; 183 // debug_printf("closing(pcb: %p)\n", pcb); 184 tcp_close(pcb); 185 tcp_arg(pcb, NULL); 186 tcp_sent(pcb, NULL); 187 tcp_recv(pcb, NULL); 188 debug_printf("connection closed\n"); 189 // wait_for_condition(); 190} 191 192 193/* -------------- File receiving ----------------------*/ 194 195static FILE* recv_f; 196 197static err_t tcp_is_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, 198 err_t err) 199{ 200 // debug_printf("tcp is recv\n"); 201 202 static int tot = 0; 203 204 int len; 205 char *payload; 206 207 if (pb == NULL) { 208 // connection closed. clean up and then EXIT the program. 209 debug_printf("finished receiving file. %d bytes\n", tot); 210 close_connection(pcb); 211 fflush(recv_f); 212 fclose(recv_f); 213 exit(EXIT_SUCCESS); 214 } else if ((err == ERR_OK) && (pb != NULL)) { 215 216 // pointer to the payload 217 payload = (char *)pb->payload; 218 219 // size of the payload 220 len = pb->tot_len; 221 222 // debug_printf("Got data [%d bytes]\n", len); 223 // printf("data: %s\n", payload); 224 225 // write out to file 226 int n = fwrite(payload, 1, len, recv_f); 227 fflush(recv_f); 228 // debug_printf("wrote %d bytes\n", n); 229 230 tot += n; 231 232 // debug_printf("for a total of: %d bytes\n", tot); 233 234 // Inform TCP that we have taken the data. 235 tcp_recved(pcb, pb->tot_len); 236 237 // Free the packet buffer 238 pbuf_free(pb); 239 } 240 241 // debug_printf("done tcp is recv\n"); 242 243 return ERR_OK; 244} 245 246 247static errval_t do_receive_file(int port, char *path) 248{ 249 250 assert(path != NULL); 251 252 errval_t err; 253 254 debug_printf("receive file %s on port %d\n", path, port); 255 256 // debug_printf("opening %s for writing\n", path); 257 258 // open file to receive 259 recv_f = fopen(path, "w"); 260 if (recv_f == NULL) { 261 err = errno; 262 fprintf(stderr, "failed to fopen %s for writing\n", path); 263 return err; 264 } 265 266 // debug_printf("%s successfully opened\n", path); 267 268 // debug_printf("binding\n"); 269 270 struct tcp_pcb *pcb; 271 pcb = bind(port); 272 if (pcb == NULL) { 273 assert(pcb != NULL); 274 // return SOME_ERR; 275 } 276 277 // debug_printf("bound\n"); 278 279 return SYS_ERR_OK; 280} 281 282 283/* -------------- File sending ----------------------*/ 284 285static errval_t send_message(struct tcp_pcb *pcb, void *msg, size_t len); 286 287 288static err_t tcp_is_sent(void *arg, struct tcp_pcb *pcb, u16_t len) 289{ 290 assert(pcb != NULL); 291 292 // debug_printf("sent %u bytes.\n", len); 293 294 wait_cond = false; 295 296 return ERR_OK; 297} 298 299 300// FIX: make this non-recursive 301static errval_t send_message(struct tcp_pcb *pcb, void *msg, size_t len) 302{ 303 err_t err; 304 305 // debug_printf("send_message(pcb: %p, msg: %p, len: %d)\n", 306 // pcb, msg, (int)len); 307 308 if (len > 0) { 309 wait_cond = true; 310 uint16_t send_size = MIN(tcp_sndbuf(pcb), len); 311 // debug_printf("\tsend_size: %d.\n", send_size); 312 if (send_size <= 0) { 313 // debug_printf("\tnot enough space in sndbuf. will retry later.\n"); 314 // ran out of memory, wait for a send to happen 315 // debug_printf("\twaiting for condition.\n"); 316 wait_for_condition(); 317 // and try again 318 // debug_printf("\tand trying again.\n"); 319 send_message(pcb, msg, len); 320 } else { 321 // debug_printf("\tsent %d bytes of %lu.\n", send_size, len); 322 err = tcp_write(pcb, msg, send_size, TCP_WRITE_FLAG_COPY); 323 // TODO: proper error handling 324 if (err != ERR_OK) { 325 fprintf(stderr, "error writing %d\n", err); 326 return LWIP_ERR_MEM; //TODO: what errno to use? 327 } 328 if (send_size < len) { 329 // debug_printf("\tdidn't send whole message, so going to send rest now\n"); 330 send_message(pcb, msg + send_size, len - send_size); 331 } 332 } 333 } 334 335 err = tcp_output(pcb); 336 337 // TODO: proper error handling 338 if (err != ERR_OK) { 339 fprintf(stderr, "error in tcp_output %d\n", err); 340 return LWIP_ERR_MEM; //TODO: what errno to use? 341 } 342 343 // debug_printf("done send_message()\n"); 344 345 return SYS_ERR_OK; 346} 347 348#define BUFLEN 2048 349static const int buflen = BUFLEN; 350//static char buf[buflen]; 351static char buf[BUFLEN]; 352 353 354 355static errval_t send_file(struct tcp_pcb *pcb, char *path) 356{ 357 358 assert(pcb != NULL); 359 assert(path != NULL); 360 361 // debug_printf("send_file(pcb: %p, path: %s)\n", pcb, path); 362 363 FILE *f; 364 errval_t err; 365 366 // debug_printf("opening file %s...", path); 367 368 // open the file 369 f = fopen(path, "r"); 370 if (f == NULL) { 371 err = errno; 372 fprintf(stderr, "failed to fopen %s\n", path); 373 return err; 374 } 375 376 // debug_printf("done.\n"); 377 378 // debug_printf("going to send file\n"); 379 380 // int i = 0; 381 int n; 382 while ((n = fread(buf, 1, buflen, f)) > 0) { 383 // debug_printf("sending part %d\n", i); i++; 384 err = send_message(pcb, buf, n); 385 if (err_is_fail(err)) { 386 fprintf(stderr, "failed while sending message: %d\n", (int)err); 387 // debug_printf("going to close f: %p\n", f); 388 fclose(f); 389 wait_cond = false; 390 return err; 391 } 392 } 393 394 // debug_printf("going to close f: %p\n", f); 395 fclose(f); 396 397 // debug_printf("done send_file\n"); 398 399 wait_cond = false; 400 401 return SYS_ERR_OK; 402} 403 404 405static errval_t do_send_file(ip_addr_t *addr, int port, char *path) 406{ 407 assert(addr != NULL); 408 assert(path != NULL); 409 410 errval_t err; 411 412 debug_printf("send file %s to %s:%d\n", path, ipaddr_ntoa(addr), port); 413 414 ip_addr_t ip; 415 ip_addr_copy(ip, *addr); 416 417 // debug_printf("ready to connect\n"); 418 419 struct tcp_pcb *pcb; 420 pcb = connect(&ip, port); 421 if (pcb == NULL) { 422 assert(pcb != NULL); 423 // return SOME_ERR; 424 } 425 426 // debug_printf("connected\n"); 427 428 // debug_printf("start sending.\n"); 429 430 wait_cond = true; 431 err = send_file(pcb, path); 432 if (err_is_fail(err)) { 433 return err; 434 } 435 436 wait_for_condition(); 437 438 debug_printf("send finished.\n"); 439 440 // debug_printf("closing connection.\n"); 441 442 // close_connection(pcb); 443 444 debug_printf("connection closed.\n"); 445 446 return SYS_ERR_OK; 447} 448 449 450 451/* ------------------- Main -------------------------*/ 452 453struct args { 454 ip_addr_t addr; 455 bool addr_set; 456 int port; 457 bool send; 458 char *path; 459}; 460 461static struct args process_args(int argc, char *argv[]) 462{ 463 struct args res = (struct args) { 464 .addr_set = false, 465 .port = 0, 466 .send = false, 467 .path = NULL, 468 }; 469 470 int opt; 471 size_t flen; 472 473 while ((opt = getopt(argc, argv, "sra:p:f:")) != -1) { 474 475 switch (opt) { 476 case 's': 477 // send 478 res.send = true; 479 break; 480 case 'r': 481 // receive 482 res.send = false; 483 break; 484 case 'a': 485 // IP address 486 if (ipaddr_aton(optarg, &res.addr) == 0) { 487 fprintf(stderr, "Invalid IP addr: %s\n", optarg); 488 goto fail; 489 } 490 res.addr_set = true; 491 break; 492 case 'p': 493 // port 494 res.port = atoi(optarg); 495 break; 496 case 'f': 497 // file name 498 flen = strlen(optarg); 499 res.path = malloc(flen + 1); 500 if (res.path == NULL) { 501 fprintf(stderr, "failed to allocate memory\n"); 502 goto fail; 503 } 504 strncpy(res.path, optarg, flen+1); 505 break; 506 default: 507 goto fail; 508 } 509 } 510 511 // check conditions for correct arguments 512 if ((res.path == NULL) || (res.send && !res.addr_set)) { 513 goto fail; 514 } 515 516 return res; 517 518 fail: 519 fprintf(stderr, "Usage: %s [-sr] [-a address] [-p port] [-f file]\n", 520 argv[0]); 521 exit(EXIT_FAILURE); 522 return res; 523} 524 525// this is a hack to force vfs library code to be included 526extern void vfs_dummy(void); 527 528int main(int argc, char *argv[]) 529{ 530 531 struct args args = process_args(argc, argv); 532 533 // debug_printf("IP addr: %s port: %d\n", inet_ntoa(args.addr), args.port); 534 // debug_printf("%s file %s\n", args.send? "send": "receive", args.path); 535 536 // Boot up 537 // debug_printf("calling stack_init()\n"); 538 539 networking_init_default(); 540 541 // debug_printf("back from stack_init()\n"); 542 543 vfs_dummy(); 544 545 if (args.send) { 546 do_send_file(&args.addr, args.port, args.path); 547 debug_printf("do_send_file finished\n"); 548 } else { 549 do_receive_file(args.port, args.path); 550 } 551 552#if 0 553 // start event loop 554 errval_t err; 555 struct waitset *ws = get_default_waitset(); 556 while (true) { 557 err = event_dispatch(ws); 558 if (err_is_fail(err)) { 559 DEBUG_ERR(err, "in event_dispatch"); 560 break; 561 } 562 } 563#endif 564 565 return EXIT_SUCCESS; 566} 567 568 569 570