1/*
2 * $Id: aecho.c,v 1.9 2009-10-14 01:38:28 didg Exp $
3 *
4 * Copyright (c) 1990,1991 Regents of The University of Michigan.
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation for any purpose and without fee is hereby granted,
9 * provided that the above copyright notice appears in all copies and
10 * that both that copyright notice and this permission notice appear
11 * in supporting documentation, and that the name of The University
12 * of Michigan not be used in advertising or publicity pertaining to
13 * distribution of the software without specific, written prior
14 * permission. This software is supplied as is without expressed or
15 * implied warranties of any kind.
16 *
17 *	Research Systems Unix Group
18 *	The University of Michigan
19 *	c/o Mike Clark
20 *	535 W. William Street
21 *	Ann Arbor, Michigan
22 *	+1-313-763-0525
23 *	netatalk@itd.umich.edu
24 */
25
26/*
27 * AppleTalk Echo Protocol Client
28 */
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif /* HAVE_CONFIG_H */
33
34#include <sys/types.h>
35#include <sys/param.h>
36#include <sys/socket.h>
37#include <sys/time.h>
38#include <errno.h>
39
40#include <signal.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#ifdef HAVE_NETDB_H
45#include <netdb.h>
46#endif /* HAVE_NETDB_H */
47
48#include <netatalk/endian.h>
49#include <netatalk/at.h>
50#include <atalk/compat.h>
51#include <atalk/aep.h>
52#include <atalk/nbp.h>
53#include <atalk/netddp.h>
54#include <atalk/ddp.h>
55#include <atalk/util.h>
56
57/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature */
58#ifndef SOCKLEN_T
59#define SOCKLEN_T unsigned int
60#endif /* ! SOCKLEN_T */
61
62static struct sockaddr_at	target;
63static int			sock;
64static unsigned int		nsent = 0, nrecv = 0;
65static time_t			totalms = 0, minms = -1, maxms = -1;
66static unsigned int     	pings = 0;
67
68static void usage(char *);
69
70static void done(int sig _U_)
71{
72    if ( nsent) {
73	printf( "\n----%d.%d AEP Statistics----\n",
74		ntohs( target.sat_addr.s_net ), target.sat_addr.s_node );
75	printf( "%d packets sent, %d packets received, %d%% packet loss\n",
76		nsent, nrecv, (( nsent - nrecv ) * 100 ) / nsent );
77	if ( nrecv ) {
78	    printf( "round-trip (ms)  min/avg/max = %ld/%ld/%ld\n",
79		    minms, totalms / nrecv, maxms );
80	}
81    }
82    exit( 0 );
83}
84
85static void aep_send(int sig _U_)
86{
87    struct timeval	tv;
88    char		*p, buf[ 1024 ];
89    static unsigned int	seq = 0;
90
91    p = buf;
92    *p++ = DDPTYPE_AEP;
93    *p++ = AEPOP_REQUEST;
94    memcpy( p, &seq, sizeof( unsigned int ));
95    p += sizeof( unsigned int );
96    seq++;
97
98    if ( gettimeofday( &tv, (struct timezone *)0 ) < 0 ) {
99	perror( "gettimeofday" );
100	exit( 1 );
101    }
102    memcpy( p, &tv, sizeof( struct timeval ));
103    p += sizeof( struct timeval );
104
105    if ( netddp_sendto( sock, buf, p - buf, 0, (struct sockaddr *) &target,
106	    sizeof( struct sockaddr_at )) < 0 ) {
107	perror( "sendto" );
108	exit( 1 );
109    }
110    nsent++;
111    if (pings && nsent > pings) done(0);
112}
113
114int main(int ac, char **av)
115{
116    struct servent	*se;
117    struct sigaction	sv;
118    struct itimerval	it;
119    struct sockaddr_at	sat, saddr;
120    struct timeval	tv, atv;
121    struct nbpnve	nn;
122    char		*obj = NULL, *type = "Workstation", *zone = "*";
123    int			cc;
124    SOCKLEN_T		satlen;
125    unsigned int	seq;
126    time_t		ms;
127    char		buf[ 1024 ], *p;
128    unsigned char	port;
129
130    extern char		*optarg;
131    extern int		optind;
132
133    memset(&saddr, 0, sizeof(saddr));
134    while (( cc = getopt( ac, av, "c:A:" )) != EOF ) {
135	switch ( cc ) {
136	case 'A':
137	    if (!atalk_aton(optarg, &saddr.sat_addr)) {
138	        fprintf(stderr, "Bad address.\n");
139		exit(1);
140	    }
141	    break;
142
143	  case 'c' :
144	    pings = atoi( optarg );
145	    break;
146
147	default :
148	    usage( av[ 0 ] );
149	    exit( 1 );
150	}
151    }
152    if ( ac - optind != 1 ) {
153	usage( av[ 0 ] );
154	exit( 1 );
155    }
156
157    /*
158     * Save the port, since nbp_lookup calls getservbyname() to get the
159     * nbp port.
160     */
161    if (( se = getservbyname( "echo", "ddp" )) == NULL )
162       port = 4;
163    else
164       port = ntohs( se->s_port );
165
166    memset( &target, 0, sizeof( struct sockaddr_at ));
167#ifdef BSD4_4
168    target.sat_len = sizeof( struct sockaddr_at );
169#endif /* BSD4_4 */
170    target.sat_family = AF_APPLETALK;
171    if ( !atalk_aton( av[ optind ], &target.sat_addr )) {
172	if ( nbp_name( av[ optind ], &obj, &type, &zone ) || !obj ) {
173	    fprintf( stderr, "Bad name: %s\n", av[ optind ] );
174	    exit( 1 );
175	}
176	if ( nbp_lookup( obj, type, zone, &nn, 1, &saddr.sat_addr) <= 0 ) {
177	    fprintf( stderr, "Can't find: %s\n", av[ optind ] );
178	    exit( 1 );
179	}
180	memcpy( &target, &nn.nn_sat, sizeof( struct sockaddr_at ));
181    }
182    target.sat_port = port;
183
184    if ((sock = netddp_open(saddr.sat_addr.s_net || saddr.sat_addr.s_node ?
185			 &saddr : NULL, NULL)) < 0) {
186       perror("ddp_open");
187       exit(1);
188    }
189
190    sv.sa_handler = aep_send;
191    sigemptyset( &sv.sa_mask );
192    sigaddset( &sv.sa_mask, SIGINT );
193    sv.sa_flags = SA_RESTART;
194    if ( sigaction( SIGALRM, &sv, (struct sigaction *)0 ) < 0 ) {
195	perror( "sigaction" );
196	exit( 1 );
197    }
198
199    sv.sa_handler = done;
200    sigemptyset( &sv.sa_mask );
201    sigaddset( &sv.sa_mask, SIGALRM );
202    sv.sa_flags = SA_RESTART;
203    if ( sigaction( SIGINT, &sv, (struct sigaction *)0 ) < 0 ) {
204	perror( "sigaction" );
205	exit( 1 );
206    }
207
208    it.it_interval.tv_sec = 1L;
209    it.it_interval.tv_usec = 0L;
210    it.it_value.tv_sec = 1L;
211    it.it_value.tv_usec = 0L;
212
213    if ( setitimer( ITIMER_REAL, &it, (struct itimerval *)0 ) < 0 ) {
214	perror( "setitimer" );
215	exit( 1 );
216    }
217
218    for (;;) {
219	satlen = sizeof( struct sockaddr_at );
220	if (( cc = netddp_recvfrom( sock, buf, sizeof( buf ), 0,
221				    (struct sockaddr *) &sat,
222				    &satlen )) < 0 ) {
223	    if ( errno == EINTR ) {
224		errno = 0;
225		continue;
226	    } else {
227		perror( "recvfrom" );
228		exit( 1 );
229	    }
230	}
231	p = buf;
232	if ( *p++ != DDPTYPE_AEP || *p++ != AEPOP_REPLY ) {
233	    fprintf( stderr, "%s: bad packet!\n", av[ 0 ] );
234	    continue;
235	}
236	if ( gettimeofday( &tv, (struct timezone *)0 ) < 0 ) {
237	    perror( "gettimeofday" );
238	    exit( 1 );
239	}
240	memcpy( &seq, p, sizeof( unsigned int ));
241	p += sizeof( unsigned int );
242	memcpy( &atv, p, sizeof( struct timeval ));
243	nrecv++;
244	ms = ( tv.tv_sec - atv.tv_sec ) * 1000 +
245		( tv.tv_usec - atv.tv_usec ) / 1000;
246	totalms += ms;
247	if ( ms > maxms ) {
248	    maxms = ms;
249	}
250	if ( ms < minms || minms == -1 ) {
251	    minms = ms;
252	}
253	printf( "%d bytes from %u.%u: aep_seq=%d. time=%ld. ms\n",
254		cc, ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
255		seq, ms );
256        if (pings && seq + 1 >= pings) done(0);
257    }
258}
259
260static void usage( char * av0 )
261{
262    fprintf( stderr, "usage:\t%s [-A source address ] [-c count] ( addr | nbpname )\n", av0 );
263    exit( 1 );
264}
265