blob: 64fa3f2cc872fa12798867995c390b17c3f99655 [file] [log] [blame]
Austin Schuh8d0a2852019-12-28 22:54:28 -08001/* 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 <errno.h>
55#include <netinet/sctp.h>
56
57#define BUFSIZE 1024
58static char buffer[BUFSIZE];
59#define DUMP_CORE { char *diediedie = 0; *diediedie = 0; }
60
61typedef enum { COMMAND_NONE, COMMAND_RECV, COMMAND_SEND } command_t;
62
63/* These are the global options. */
64#define MAX_NUM_HOST 5
65static char *local_host[MAX_NUM_HOST];
66static int num_local_host = 0;
67static int local_port = 4444;
68
69static int buffer_size = BUFSIZE;
70static char *remote_host = NULL;
71static int remote_port = 4444;
72static command_t command = COMMAND_NONE;
73static char *filename = NULL;
74static int interactive = 0;
75static unsigned long delay = 0;
76static int verbose = 0;
77
78static void
79usage(char *argv0)
80{
81 fprintf(stderr, "Usage: %s [options]\n",argv0);
82 fprintf(stderr, "Options:\n");
83 fprintf(stderr, "\t--local, -H <hostname> Specify local interface\n");
84 fprintf(stderr, "\t--local-port, -P <port> Specify local port (default 4444)\n");
85 fprintf(stderr, "\t--remote, -h <hostname> Specify interface on remote host\n");
86 fprintf(stderr, "\t--remote-port, -p <port> Specify remote port (default 4444)\n");
87 fprintf(stderr, "\t--listen, -l Work in receiving mode\n");
88 fprintf(stderr, "\t--send, -s Work in sending mode\n");
89 fprintf(stderr, "\t--file, -f <filename> File to read or write,\n");
90 fprintf(stderr, "\t--buffer, -b <size> Buffer size. (default 1024 bytes)\n");
91 fprintf(stderr, "\t by default use standard input/output.\n");
92 fprintf(stderr, "\t--quick, -q Send packets continueously,\n");
93 fprintf(stderr, "\t do not wait for <ENTER> key. Default wait.\n");
94 fprintf(stderr, "\t--delay, -d <usec> Delay between consecutive sends (see --quick)\n");
95 fprintf(stderr, "\t--verbose, -v In verbose mode, display the progress.\n");
96 fprintf(stderr, "\n\t--help, Print this message.\n\n");
97} /* usage() */
98
99static int parse_arguments(int argc, char *argv[])
100{
101 int option_index = 0;
102 int c;
103 static struct option long_options[] = {
104 {"local", 1, 0, 1},
105 {"local-port", 1, 0, 2},
106 {"remote", 1, 0, 3},
107 {"remote-port", 1, 0, 4},
108 {"file", 1, 0, 5},
109 {"delay", 1, 0, 6},
110 {"buffer", 1, 0, 7},
111 {"listen", 0, 0, 10},
112 {"send", 0, 0, 11},
113 {"quick", 0, 0, 12},
114 {"verbose", 0, 0, 13},
115 {"help", 0, 0, 99},
116 {0, 0, 0, 0}
117 };
118
119 /* Parse the arguments. */
120 while (1) {
121 c = getopt_long(argc, argv, "H:P:h:p:f:d:b:qlsv",long_options,&option_index);
122 if (c == -1) break;
123
124 switch (c) {
125 case 0:
126 printf ("option %s", long_options[option_index].name);
127 if (optarg) printf (" with arg %s", optarg);
128 printf ("\n");
129 break;
130 case 1: /* local host */
131 case 'H':
132 local_host[num_local_host++] = optarg;
133 break;
134 case 2: /* local port */
135 case 'P':
136 local_port = atoi(optarg);
137 break;
138 case 3: /* remote host */
139 case 'h':
140 remote_host = optarg;
141 break;
142 case 4: /* remote port */
143 case 'p':
144 remote_port = atoi(optarg);
145 break;
146 case 5:
147 case 'f':
148 filename = optarg;
149 break;
150
151 case 6:
152 case 'd':
153 delay = strtoul(optarg,NULL,10);
154 printf("delay is %ld usec\n",delay);
155 break;
156
157 case 7:
158 case 'b':
159 buffer_size = atoi(optarg);
160 if ( buffer_size > BUFSIZE ) {
161 buffer_size = BUFSIZE;
162 fprintf(stderr,"Warning, buffer size too large, set to %d\n",buffer_size);
163 }
164 break;
165
166 case 12:
167 case 'q': interactive = 0; break;
168
169 case 13:
170 case 'v': verbose = 1; break;
171 /* COMMANDS */
172 case 10: /* listen */
173 case 'l':
174 if (command) {
175 fprintf(stderr, "%s: pick ONE of listen or send\n", argv[0]);
176 return 1;
177 }
178 else command = COMMAND_RECV;
179 break;
180
181 case 11: /* send */
182 case 's':
183 if (command) {
184 fprintf(stderr, "%s: pick ONE of listen or send\n", argv[0]);
185 return 2;
186 } else command = COMMAND_SEND;
187 break;
188
189 case '?':
190 case 99:
191 usage(argv[0]);
192 return 3;
193 break;
194
195 default:
196 printf ("%s: unrecognized option 0%c\n", argv[0], c);
197 usage(argv[0]);
198 return 4;
199 }
200 }
201
202 if (optind < argc) {
203 fprintf(stderr, "%s: non-option arguments are illegal: ", argv[0]);
204 while (optind < argc) fprintf(stderr, "%s ", argv[optind++]);
205 fprintf(stderr, "\n");
206 usage(argv[0]);
207 return 5;
208 }
209
210
211 if (0 == num_local_host) {
212 fprintf(stderr, "%s: You MUST provide a local host.\n", argv[0]);
213 usage(argv[0]);
214 return 6;
215 }
216
217 if ( filename == NULL && command == COMMAND_SEND)
218 fprintf(stderr,"%s: Use standard input to send\n",argv[0]);
219
220 if ( filename == NULL && command == COMMAND_RECV )
221 fprintf(stderr,"%s: Use standard output to write\n",argv[0]);
222
223 return 0;
224} /* parse_arguments() */
225
226static void
227emsg(char *prog,char *s)
228{
229 if ( prog != NULL ) fprintf(stderr,"%s: ",prog);
230 perror(s);
231 fflush(stdout);
232 //DUMP_CORE;
233
234 exit(-1);
235}
236
237static int build_endpoint(char *argv0)
238{
239 int retval,i;
240
241 /* Create the local endpoint. */
242 if ( (retval = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0 ) {
243 emsg(argv0,"socket");
244 exit(retval);
245 }
246
247 for ( i = 0;i < num_local_host;i++ ) {
248 struct hostent *hst;
249 struct sockaddr_in laddr;
250
251 memset(&laddr, 0, sizeof(laddr));
252 /* Get the transport address for the local host name. */
253 fprintf(stderr,"Hostname %d is %s\n",i+1,local_host[i]);
254 if ( (hst = gethostbyname(local_host[i])) == NULL ) {
255 fprintf(stderr, "%s: bad hostname: %s\n", argv0, local_host[i]);
256 exit(1);
257 }
258 memcpy(&laddr.sin_addr, hst->h_addr_list[0],sizeof(laddr.sin_addr));
259 laddr.sin_port = htons(local_port);
260 laddr.sin_family = AF_INET;
261
262 /* Bind this socket to the test port. */
263 if ( bind(retval, (struct sockaddr *)&laddr, sizeof(laddr)) ) {
264 emsg(argv0,"bind");
265 exit(-1);
266 }
267 }
268
269 fprintf(stderr,"Endpoint built.\n");
270
271 return retval;
272} /* build_endpoint() */
273
274/* Convenience structure to determine space needed for cmsg. */
275typedef union {
276 struct sctp_initmsg init;
277 struct sctp_sndrcvinfo sndrcvinfo;
278} _sctp_cmsg_data_t;
279
280
281/* Listen on the socket, printing out anything that arrives. */
282static void
283command_recv(char *argv0, int sk)
284{
285 struct msghdr inmessage;
286 char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
287 struct iovec iov;
288 int ret;
289 int fd;
290 int ct;
291
292 if (listen(sk, 1) == -1)
293 emsg(argv0, "listen");
294 /* Initialize inmessage with enough space for DATA... */
295 memset(&inmessage, 0, sizeof(inmessage));
296 iov.iov_base = buffer;
297 iov.iov_len = buffer_size;
298 inmessage.msg_iov = &iov;
299 inmessage.msg_iovlen = 1;
300 /* or a control message. */
301 inmessage.msg_control = incmsg;
302 inmessage.msg_controllen = sizeof(incmsg);
303
304 /* creat a file */
305 if ( filename == NULL ) fd = 1;
306 else if ( (fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE)) == -1 )
307 emsg(argv0,"open");
308
309 fprintf(stderr,"%s Receiving...\n", argv0);
310 /* Get the messages sent */
311 ct = 0;
312 while ( (ret = recvmsg(sk, &inmessage, MSG_WAITALL)) >= 0 ) {
313 if ( verbose )
314 fprintf(stderr,"%s-%d received %d bytes\n",
315 argv0, ++ct, ret);
316 if ( !(inmessage.msg_flags & MSG_NOTIFICATION) ) {
317 //printf("%s write %d bytes\n",argv0,ret);
318 if ( write(fd,buffer,ret) != ret ) emsg(argv0,"write");
319 } else {
320 union sctp_notification *sn;
321 sn = (union sctp_notification *)iov.iov_base;
322 if ((sn->sn_header.sn_type == SCTP_ASSOC_CHANGE) &&
323 (sn->sn_assoc_change.sac_state
324 == SCTP_SHUTDOWN_COMP))
325 break;
326 }
327
328 }
329
330 if ( ret < 0 ) emsg(argv0,"recvmsg");
331
332 close(fd);
333 close(sk);
334} /* command_recv() */
335
336/* Read lines from stdin and send them to the socket. */
337static void
338command_send(char *argv0, int sk)
339{
340 struct msghdr outmsg;
341 struct iovec iov;
342 struct hostent *hst;
343 struct sockaddr_in remote_addr;
344 int fd;
345 int msglen;
346 int ct;
347
348 /* Set up the destination. */
349 hst = gethostbyname(remote_host);
350 if (hst == NULL || hst->h_length < 1) {
351 fprintf(stderr, "%s: bad hostname: %s\n", argv0, remote_host);
352 exit(1);
353 }
354 memcpy(&remote_addr.sin_addr, hst->h_addr_list[0], sizeof(remote_addr.sin_addr));
355 remote_addr.sin_port = htons(remote_port);
356 remote_addr.sin_family = AF_INET;
357
358 /* Initialize the message struct we use to pass messages to
359 * the remote socket.
360 */
361 iov.iov_base = buffer;
362 iov.iov_len = buffer_size;
363 outmsg.msg_iov = &iov;
364 outmsg.msg_iovlen = 1;
365 outmsg.msg_control = NULL;
366 outmsg.msg_controllen = 0;
367 outmsg.msg_name = &remote_addr;
368 outmsg.msg_namelen = sizeof(remote_addr);
369
370 /* open the file */
371 if ( filename == NULL ) fd = 0;
372 else if ( (fd = open(filename,O_RDONLY)) == -1 ) emsg(argv0,"open");
373
374 fprintf(stderr,"%s ready to send...\n", argv0);
375 ct = 0;
376 while ( (msglen = read(fd,buffer,buffer_size)) > 0 ) {
377 /* Send to our neighbor. */
378 iov.iov_len = msglen;
379 if ( sendmsg(sk, &outmsg, 0) != msglen ) emsg(argv0,"sendmsg");
380 if ( verbose ) fprintf(stderr,"%s-%d send %d bytes\n",argv0,++ct,msglen);
381 if ( interactive && fd != 1 )
382 getchar();
383 // no flow control? no problem, make it slow
384 else if ( delay > 0 )
385 usleep(delay);
386 }
387
388 close(fd);
389 close(sk);
390} /* command_send() */
391
392int main(int argc, char *argv[])
393{
394 int ret;
395
396 if (( ret = parse_arguments(argc, argv) )) return ret;
397
398 switch(command) {
399 case COMMAND_NONE:
400 fprintf(stderr, "%s: Please specify a command.\n", argv[0]);
401 break;
402 case COMMAND_RECV:
403 command_recv(argv[0],build_endpoint(argv[0]));
404 break;
405 case COMMAND_SEND:
406 command_send(argv[0],build_endpoint(argv[0]));
407 break;
408 default:
409 fprintf(stderr, "%s: illegal command %d\n", argv[0], command);
410 } /* switch(command) */
411
412 return 0;
413}