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