Imported Upstream version 1.0.10
[platform/upstream/lksctp-tools.git] / src / apps / myftp.c
1 /* myftp - simple file transfer over sctp testing tool. 
2  * Copyright (c) 2002 Intel Corp.
3  * 
4  * This file is part of the LKSCTP kernel Implementation.  This
5  * is a submission by Xingang Guo from the Intel Corporation while 
6  * participating on the LKSCTP project.  
7  * 
8  * The SCTP implementation is free software; 
9  * you can redistribute it and/or modify it under the terms of 
10  * the GNU General Public License as published by
11  * the Free Software Foundation; either version 2, or (at your option)
12  * any later version.
13  * 
14  * The SCTP implementation is distributed in the hope that it 
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16  *                 ************************
17  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  * See the GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with GNU CC; see the file COPYING.  If not, write to
22  * the Free Software Foundation, 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.  
24  * 
25  * Please send any bug reports or fixes you make to the
26  * email address(es):
27  *    lksctp developers <sctp-developers-list@cig.mot.com>
28  * 
29  * Or submit a bug report through the following website:
30  *    http://www.sf.net/projects/lksctp
31  *
32  * Written or modified by: 
33  *    Xingang Guo           <xingang.guo@intel.com>
34  *    Jon Grimm             <jgrimm@us.ibm.com> 
35  * 
36  * Any bugs reported given to us we will try to fix... any fixes shared will
37  * be incorporated into the next SCTP release.
38  */
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #define _GNU_SOURCE
43 #include <getopt.h>
44 #include <netdb.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47
48 #include <ctype.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/socket.h>
52 #include <sys/uio.h>
53 #include <netinet/in.h> /* for sockaddr_in */
54 #include <sys/errno.h>
55 #include <errno.h>
56 #include <netinet/sctp.h>
57
58 #define BUFSIZE 1024
59 static char buffer[BUFSIZE];
60 #define DUMP_CORE {  char *diediedie = 0; *diediedie = 0; }
61
62 typedef enum { COMMAND_NONE, COMMAND_RECV, COMMAND_SEND } command_t;
63
64 /* These are the global options.  */
65 #define MAX_NUM_HOST    5
66 static char *local_host[MAX_NUM_HOST];
67 static int num_local_host = 0;
68 static int local_port = 4444;
69
70 static int buffer_size = BUFSIZE;
71 static char *remote_host = NULL;
72 static int remote_port = 4444;
73 static command_t command = COMMAND_NONE;
74 static char *filename = NULL;
75 static int interactive = 0;
76 static unsigned long delay = 0;      
77 static int verbose = 0;
78
79 static void
80 usage(char *argv0)
81 {
82         fprintf(stderr, "Usage: %s [options]\n",argv0);
83         fprintf(stderr, "Options:\n");
84         fprintf(stderr, "\t--local, -H <hostname>     Specify local interface\n");
85         fprintf(stderr, "\t--local-port, -P <port>    Specify local port (default 4444)\n");
86         fprintf(stderr, "\t--remote, -h <hostname>    Specify interface on remote host\n");
87         fprintf(stderr, "\t--remote-port, -p <port>   Specify remote port (default 4444)\n");
88         fprintf(stderr, "\t--listen, -l               Work in receiving mode\n");
89         fprintf(stderr, "\t--send, -s                 Work in sending mode\n");
90         fprintf(stderr, "\t--file, -f <filename>      File to read or write,\n");
91         fprintf(stderr, "\t--buffer, -b <size>        Buffer size. (default 1024 bytes)\n");
92         fprintf(stderr, "\t                           by default use standard input/output.\n");
93         fprintf(stderr, "\t--quick, -q                Send packets continueously,\n");
94         fprintf(stderr, "\t                           do not wait for <ENTER> key. Default wait.\n");
95         fprintf(stderr, "\t--delay, -d <usec>         Delay between consecutive sends (see --quick)\n");
96         fprintf(stderr, "\t--verbose, -v              In verbose mode, display the progress.\n");
97         fprintf(stderr, "\n\t--help,                    Print this message.\n\n");
98 } /* usage() */
99
100 static int parse_arguments(int argc, char *argv[])
101 {
102         int option_index = 0;
103         int c;
104         static struct option long_options[] = {
105                 {"local",       1, 0, 1},
106                 {"local-port",  1, 0, 2},
107                 {"remote",      1, 0, 3},
108                 {"remote-port", 1, 0, 4},
109                 {"file",        1, 0, 5},
110                 {"delay",       1, 0, 6},
111                 {"buffer",      1, 0, 7},
112                 {"listen",      0, 0, 10},
113                 {"send",        0, 0, 11},
114                 {"quick",       0, 0, 12},
115                 {"verbose",     0, 0, 13},
116                 {"help",        0, 0, 99},
117                 {0,             0, 0, 0}
118         };
119
120         /* Parse the arguments.  */
121         while (1) {
122                 c = getopt_long(argc, argv, "H:P:h:p:f:d:b:qlsv",long_options,&option_index);
123                 if (c == -1) break;
124
125                 switch (c) {
126                 case 0:
127                         printf ("option %s", long_options[option_index].name);
128                         if (optarg) printf (" with arg %s", optarg);
129                         printf ("\n");
130                         break;
131                 case 1:         /* local host */
132                 case 'H':
133                         local_host[num_local_host++] = optarg;
134                         break;
135                 case 2:         /* local port */
136                 case 'P':
137                         local_port = atoi(optarg);
138                         break;
139                 case 3:         /* remote host */
140                 case 'h':
141                         remote_host = optarg;
142                         break;
143                 case 4:         /* remote port */
144                 case 'p':
145                         remote_port = atoi(optarg);
146                         break;
147                 case 5:
148                 case 'f':
149                         filename = optarg;
150                         break;
151
152                 case 6:
153                 case 'd':
154                         delay = strtoul(optarg,NULL,10);
155                         printf("delay is %ld usec\n",delay);
156                         break;
157
158                 case 7:
159                 case 'b':
160                         buffer_size = atoi(optarg);
161                         if ( buffer_size > BUFSIZE ) {
162                                 buffer_size = BUFSIZE;
163                                 fprintf(stderr,"Warning, buffer size too large, set to %d\n",buffer_size);
164                         }
165                 case 12:
166                 case 'q':       interactive = 0; break;
167
168                 case 13:
169                 case 'v':       verbose = 1; break;
170                         /* COMMANDS */
171                 case 10:        /* listen */
172                 case 'l':
173                         if (command) {
174                                 fprintf(stderr, "%s: pick ONE of listen or send\n", argv[0]);
175                                 return 1;
176                         }
177                         else command = COMMAND_RECV;
178                         break;
179
180                 case 11:        /* send */
181                 case 's':
182                         if (command) {
183                                 fprintf(stderr, "%s: pick ONE of listen or send\n", argv[0]);
184                                 return 2;
185                         } else command = COMMAND_SEND;
186                         break;
187
188                 case '?':
189                 case 99:
190                         usage(argv[0]);
191                         return 3;
192                         break;
193
194                 default:
195                         printf ("%s: unrecognized option 0%c\n", argv[0], c);
196                         usage(argv[0]);
197                         return 4;
198                 }
199         }
200
201         if (optind < argc) {
202                 fprintf(stderr, "%s: non-option arguments are illegal: ", argv[0]);
203                 while (optind < argc) fprintf(stderr, "%s ", argv[optind++]);
204                 fprintf(stderr, "\n");
205                 usage(argv[0]);
206                 return 5;
207         }
208
209
210         if (0 == num_local_host) {
211                 fprintf(stderr, "%s: You MUST provide a local host.\n", argv[0]);
212                 usage(argv[0]);
213                 return 6;
214         }
215
216         if ( filename == NULL && command == COMMAND_SEND)
217                 fprintf(stderr,"%s: Use standard input to send\n",argv[0]);
218
219         if ( filename == NULL && command == COMMAND_RECV )
220                 fprintf(stderr,"%s: Use standard output to write\n",argv[0]);
221
222         return 0;
223 } /* parse_arguments() */
224
225 static void
226 emsg(char *prog,char *s)
227 {
228         if ( prog != NULL ) fprintf(stderr,"%s: ",prog);
229         perror(s);
230         fflush(stdout);
231         //DUMP_CORE;
232
233         exit(-1);
234 }
235
236 static int build_endpoint(char *argv0)
237 {
238         int retval,i;
239
240         /* Create the local endpoint.  */
241         if ( (retval = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0 ) {
242                 emsg(argv0,"socket");
243                 exit(retval);
244         }
245
246         for ( i = 0;i < num_local_host;i++ ) {
247                 struct hostent *hst;
248                 struct sockaddr_in laddr;
249
250                 /* Get the transport address for the local host name.  */
251                 fprintf(stderr,"Hostname %d is %s\n",i+1,local_host[i]);
252                 if ( (hst = gethostbyname(local_host[i])) == NULL ) {
253                         fprintf(stderr, "%s: bad hostname: %s\n", argv0, local_host[i]);
254                         exit(1);
255                 }
256                 memcpy(&laddr.sin_addr, hst->h_addr_list[0],sizeof(laddr.sin_addr));
257                 laddr.sin_port = htons(local_port);
258                 laddr.sin_family = AF_INET;
259
260                 /* Bind this socket to the test port.  */
261                 if ( bind(retval, (struct sockaddr *)&laddr, sizeof(laddr)) ) {
262                         emsg(argv0,"bind");
263                         exit(-1);
264                 }
265         }
266
267         fprintf(stderr,"Endpoint built.\n");
268
269         return retval;
270 } /* build_endpoint() */
271
272 /* Convenience structure to determine space needed for cmsg. */
273 typedef union {
274         struct sctp_initmsg init;
275         struct sctp_sndrcvinfo sndrcvinfo;
276 } _sctp_cmsg_data_t;
277
278
279 /* Listen on the socket, printing out anything that arrives.  */
280 static void
281 command_recv(char *argv0, int sk)
282 {
283         struct msghdr inmessage;
284         char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
285         struct iovec iov;
286         int ret;
287         int fd;
288         int ct;
289
290         listen(sk, 1);
291         /* Initialize inmessage with enough space for DATA... */
292         memset(&inmessage, 0, sizeof(inmessage));
293         iov.iov_base = buffer;
294         iov.iov_len = buffer_size;
295         inmessage.msg_iov = &iov;
296         inmessage.msg_iovlen = 1;
297         /* or a control message.  */
298         inmessage.msg_control = incmsg;
299         inmessage.msg_controllen = sizeof(incmsg);
300
301         /* creat a file */
302         if ( filename == NULL ) fd = 1;
303         else if ( (fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE)) == -1 )
304                 emsg(argv0,"open");
305
306         fprintf(stderr,"%s Receiving...\n", argv0);
307         /* Get the messages sent */
308         ct = 0;
309         while ( (ret = recvmsg(sk, &inmessage, MSG_WAITALL)) >= 0 ) {
310                 if ( verbose )
311                         fprintf(stderr,"%s-%d received %d bytes\n",
312                                 argv0, ++ct, ret);
313                 if ( !(inmessage.msg_flags & MSG_NOTIFICATION) ) {
314                         //printf("%s write %d bytes\n",argv0,ret);
315                         if ( write(fd,buffer,ret) != ret ) emsg(argv0,"write");
316                 } else {
317                         union sctp_notification *sn;
318                         sn = (union sctp_notification *)iov.iov_base;
319                         if ((sn->sn_header.sn_type == SCTP_ASSOC_CHANGE) &&
320                             (sn->sn_assoc_change.sac_state 
321                              == SCTP_SHUTDOWN_COMP))
322                                 break;
323                 }
324                         
325         }
326
327         if ( ret < 0 ) emsg(argv0,"recvmsg");
328
329         close(fd);
330         close(sk);
331 } /* command_recv() */
332
333 /* Read lines from stdin and send them to the socket.  */
334 static void
335 command_send(char *argv0, int sk)
336 {
337         struct msghdr outmsg;
338         struct iovec iov;
339         struct hostent *hst;
340         struct sockaddr_in remote_addr;
341         int fd;
342         int msglen;
343         int ct;
344
345         /* Set up the destination.  */
346         hst = gethostbyname(remote_host);
347         if (hst == NULL || hst->h_length < 1) {
348                 fprintf(stderr, "%s: bad hostname: %s\n", argv0, remote_host);
349                 exit(1);
350         }
351         memcpy(&remote_addr.sin_addr, hst->h_addr_list[0], sizeof(remote_addr.sin_addr));
352         remote_addr.sin_port = htons(remote_port);
353         remote_addr.sin_family = AF_INET;
354
355         /* Initialize the message struct we use to pass messages to
356          * the remote socket.
357          */
358         iov.iov_base = buffer;
359         iov.iov_len = buffer_size;
360         outmsg.msg_iov = &iov;
361         outmsg.msg_iovlen = 1;
362         outmsg.msg_control = NULL;
363         outmsg.msg_controllen = 0;
364         outmsg.msg_name = &remote_addr;
365         outmsg.msg_namelen = sizeof(remote_addr);
366
367         /* open the file */
368         if ( filename == NULL ) fd = 0;
369         else if ( (fd = open(filename,O_RDONLY)) == -1 ) emsg(argv0,"open");
370
371         fprintf(stderr,"%s ready to send...\n", argv0);
372         ct = 0;
373         while ( (msglen = read(fd,buffer,buffer_size)) > 0 ) {
374                 /* Send to our neighbor.  */
375                 iov.iov_len = msglen;
376                 if ( sendmsg(sk, &outmsg, 0) != msglen ) emsg(argv0,"sendmsg");
377                 if ( verbose ) fprintf(stderr,"%s-%d send %d bytes\n",argv0,++ct,msglen);
378                 if ( interactive && fd != 1 ) 
379                         getchar();
380                         // no flow control? no problem, make it slow
381                 else if ( delay > 0 ) 
382                         usleep(delay);
383         } while ( msglen > 0 );
384
385         close(fd);
386         close(sk);
387 } /* command_send() */
388
389 int main(int argc, char *argv[])
390 {
391         int ret;
392
393         if (( ret = parse_arguments(argc, argv) )) return ret;
394
395         switch(command) {
396         case COMMAND_NONE:
397                 fprintf(stderr, "%s: Please specify a command.\n", argv[0]);
398                 break;
399         case COMMAND_RECV:
400                 command_recv(argv[0],build_endpoint(argv[0]));
401                 break;
402         case COMMAND_SEND:
403                 command_send(argv[0],build_endpoint(argv[0]));
404                 break;
405         default:
406                 fprintf(stderr, "%s: illegal command %d\n", argv[0], command);
407         } /* switch(command) */
408
409         return 0;
410 }