• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/contrib/shell_utils/
1#!perl
2#
3# asip-status - send DSIGetStatus to an AppleShare IP file server (aka
4#               ASIP, aka AFP over TCP port 548).  A returned UAM of
5#               "No User Authen" means that the server supports guest access.
6#
7# author: James W. Abendschan  <jwa@jammed.com>
8# license: GPL - http://www.gnu.org/copyleft/gpl.html
9# url: http://www.jammed.com/~jwa/hacks/security/asip/
10# Date: 7 May 1997 (v1.0) - original version
11# see also:
12#   - http://developer.apple.com/techpubs/macos8/NetworkCommSvcs/AppleShare/
13#   - http://www2.opendoor.com/asip/   (excellent Mac sharing / security site)
14#
15# todo: log in as guest & get a list of shares
16#
17
18#
19# This edition is a part of netatalk 2.2.5.
20#
21
22use strict;
23use IO::Socket;			# sucks because Timeout doesn't
24
25my ($arg);
26my ($hostport);
27my ($host);
28my ($port);
29
30while ($arg = shift @ARGV)
31{
32	$main::show_icon = 1 if ($arg eq "-i");
33	$main::debug = 1 if ($arg eq "-d");
34	$main::hexdump = 1 if ($arg eq "-x");
35	$main::showver = 1 if ($arg eq "-v");
36	$main::showver = 1 if ($arg eq "-version");
37	$main::showver = 1 if ($arg eq "--version");
38	$hostport = $arg if ($arg !~ /^-/);
39}
40
41if ($main::showver ==1)
42{
43        print "$0\n";
44        print "Original edition: 7 May 1997 \(v1.0\) James W. Abendschan\n";
45        print "This edition is a part of Netatalk 2.2.5\n";
46        exit(-1);
47}
48
49if ($hostport eq "")
50{
51	print "usage: $0 [-d] [-i] [-x] hostname[:port]\n";
52	print "       $0 -v|-version|--version\n";
53	print "Queries AFP servers for their capabilities.\n";
54	print "  -d: Enable debug output.\n";
55	print "  -i: Show icon if it exists.\n";
56	print "  -x: Enable hex dump output.\n";
57	print "  -v,-version,--version: Show version.\n";
58	exit(-1);
59}
60
61($host, $port) = split(/\:/, $hostport);
62$port = "548" if ($port eq "");
63
64my ($packet) = build_packet();
65my ($code) = sendpacket($host, $port, $packet);
66exit $code;
67
68
69sub build_packet
70{
71	my (@packet) =
72		(
73		0x00,			# 0- request, 1-reply
74		0x03,			# 3- DSIGetStatus
75		0xde, 0xad, 0x00,	# request ID
76		0x00, 0x00, 0x00, 0x00,	# data field
77		0x00, 0x00, 0x00, 0x00,	# length of data stream header
78		0x00, 0x00, 0x00, 0x00	# reserved
79		);
80
81
82        my ($packet) = pack("C*", @packet);
83
84	return $packet;
85
86}
87
88sub sendpacket
89{
90	my ($host, $port, $packet) = @_;
91	my ($b, $buf);
92
93	print "opening $host:$port\n" if ($main::debug);
94
95	my ($asip_sock) = IO::Socket::INET->new(
96		PeerAddr => $host,
97		PeerPort => $port,
98		Proto => 'tcp',
99		Type => SOCK_STREAM,
100		Timeout => 10
101		) || die "connect to $host failure: $!";
102	$asip_sock->autoflush(1);
103
104	print "sending packet\n" if ($main::debug);
105
106	my ($count) = syswrite($asip_sock, $packet, length($packet));
107
108	if ($count != length($packet))
109	{
110		print "only wrote $count of " . length($packet) . " bytes?\n";
111		exit(-1);
112	}
113
114	# reply can span multiple packets
115
116	print "sysread: " if ($main::debug);
117	while (sysread($asip_sock, $b, 256))
118	{
119		$buf .= $b;
120		print "." if ($main::debug);
121	}
122
123	close ($asip_sock);
124
125	print " read " . length($buf) . " bytes\n" if ($main::debug);
126
127	if (length($buf) == 0)
128	{
129		print "empty reply packet?\n";
130		return -2;
131	}
132	else
133	{
134		print "AFP reply from $host:$port\n";
135		return (parse_packet($buf));
136	}
137}
138
139
140sub parse_packet
141{
142	my ($buf) = shift @_;
143	my (@packet);
144	my ($i);
145
146	hexdump($buf) if ($main::hexdump);
147
148	for ($i=0;$i<length($buf);$i++)
149 	{
150 		push(@packet, substr($buf, $i, 1));
151 	}
152
153	my ($flags) = unpack("C", @packet[0]);
154	my ($cmd) = unpack("C", @packet[1]);
155
156	my ($request_id) = unpack("n", @packet[2] . @packet[3]);
157	print "Flags: $flags  Cmd: $cmd  ID: $request_id\n";
158
159	print getasipsrv("flags", $flags) . ": " . getasipsrv("command", $cmd) . "\n";
160	print "Request ID: $request_id\n";
161
162	print "** Request ID didn't match what we sent!\n" if ($request_id != 0xdead);
163
164	# "Error Code / Enclosed Data Offset"
165	# I have never seen this be non-zero ..
166
167	my ($edo) = unpack("N2", @packet[4] . @packet[5] . @packet[6] . @packet[7]);
168	print "** Wow, a non-zero Error/Enclosed Data Offset: $edo\n" if ($edo);
169
170	# "Total Data Length"
171
172	my ($datalen) = unpack("N2", @packet[8] . @packet[9] . @packet[10] . @packet[11]);
173
174	print "Total data length: $datalen\n" if ($main::debug);
175
176	# "Reserved Field"
177
178	my ($reserved) = unpack("N2", @packet[12] . @packet[13] . @packet[14] . @packet[15]);
179
180	print "Reserved field: $reserved\n" if ($reserved);
181
182	if ($cmd != 3)
183	{
184		print "I can only parse packets of reply-type DSIGetStatus (3)\n";
185		print "This is reply-type " . getasipsrv("command", $cmd) . "\n";
186	}
187	if ($datalen == 0)
188	{
189		print "No data in packet?\n";
190	}
191	if (($datalen > 0) && ($cmd == 3))
192	{
193		my (@AFPpacket) = @packet[($edo+16)..($edo+16+$datalen)];
194		return (parse_FPGetSrvrInfo(@AFPpacket));
195	}
196	else
197	{
198		print "I don't know how to parse this type of packet.\n";
199		return(2);
200	}
201}
202
203
204
205sub parse_FPGetSrvrInfo()
206{
207	my (@packet) = @_;
208	my ($i);
209
210	my ($machinetype_offset) = unpack("n", @packet[0] . @packet[1]);
211	print "Machine type offset in packet: $machinetype_offset\n" if ($main::debug);
212	my ($machinetype) = extract(1, $machinetype_offset, @packet);
213	print "Machine type: $machinetype\n";
214
215	my ($afpversioncount_offset) = unpack("n", @packet[2] . @packet[3]);
216	print "AFPversion count offset: $afpversioncount_offset\n" if ($main::debug);
217	my (@afpversions) = extract(0, $afpversioncount_offset, @packet);
218	print "AFP versions: " . join(",", @afpversions) . "\n";
219
220	my ($uamcount_offset) = unpack("n", @packet[4] . @packet[5]);
221	print "UAMcount offset: $uamcount_offset\n" if ($main::debug);
222	my (@uams) = extract(0, $uamcount_offset, @packet);
223	print "UAMs: " . join(",", @uams) . "\n";
224
225	my ($allow_guest) = 0;
226	$allow_guest = 1 if (grep(/No User Authen/, @uams));
227
228	# it would be cute to see the icon.
229
230	my ($icon_offset) = unpack("n", @packet[6] . @packet[7]);
231	print "Volume Icon & Mask offset: $icon_offset\n" if ($main::debug);
232	print "Volume Icon & Mask: exist\n" if ($icon_offset);
233
234	my ($flags) = unpack("n", @packet[8] . @packet[9]);
235	my (@flags) = parse_afp_flags($flags);
236
237	print "Flags: ";
238	print "$flags - " if ($main::debug);
239	print join(",", @flags) . "\n";
240
241	# server name starts at offset+10, length byte first.
242
243	my ($servername_len) = unpack("C1", @packet[10]);
244	my ($servername) = join("", @packet[11..(11+$servername_len-1)]);
245	print "Server name length: $servername_len\n" if ($main::debug);
246	print "Server name: $servername\n";
247
248	my ($offset) = 11 + $servername_len;
249
250 	# quietly ++ the $offset to account for the padding that happens
251 	# in the reply packet if the field names don't align on an even boundary
252
253	$offset++ if ($servername_len % 2 == 0);
254
255	print "New offset: $offset\n" if ($main::debug);
256
257	my ($signature_offset) = unpack("n2", @packet[$offset] . @packet[$offset+1]);
258	print "Signature offset: $signature_offset\n" if ($main::debug);
259	if ($signature_offset)
260	{
261                my ($signature) = join("",  @packet[$signature_offset..$signature_offset+15]);
262
263		print "Signature:\n";
264		hexdump($signature);
265	}
266
267	my ($network_address_count_offset) = unpack("n2", @packet[$offset+2] . @packet[$offset+3]);
268	print "Network address count offset: $network_address_count_offset\n" if ($main::debug);
269
270	extract_network_address($network_address_count_offset, @packet);
271
272	$offset += 4;
273	if ($flags & (1<<8)) { # Supports directory services
274		my ($directory_service_offset) = unpack("n2", @packet[$offset] . @packet[$offset+1]);
275		print "Directory service offset: $directory_service_offset\n" if ($main::debug);
276		if ($directory_service_offset)
277		{
278			my (@dirsvcs) = extract(0, $directory_service_offset, @packet);
279			while (@dirsvcs)
280			{
281				printf "Directory Service: %s\n", shift @dirsvcs;
282			}
283		}
284		$offset +=2;
285	}
286
287	if ($flags & (1<<9)) { # Supports UTF8 servername
288		my ($utf8_name_offset) = unpack("n2", @packet[$offset] . @packet[$offset+1]);
289		print "UTF8 name offset: $utf8_name_offset\n" if ($main::debug);
290		if ($utf8_name_offset)
291		{
292			my ($utf8name) = extract(1, $utf8_name_offset+1, @packet);
293			print "UTF8 Servername: $utf8name\n";
294		}
295	}
296
297	draw_icon($icon_offset, @packet) if ($main::show_icon && $icon_offset);
298
299	return $allow_guest;
300}
301
302# getsrvbyname .. sorta ..
303
304sub getasipsrv
305{
306	my ($what, $code) = @_;
307
308	if ($what eq "flags")
309	{
310		return "Request" if ($code == 0);
311		return "Reply" if ($code == 1);
312	}
313
314	if ($what eq "command")
315	{
316		return "DSICloseSession" if ($code == 1);
317		return "DSICommand" if ($code == 2);
318		return "DSIGetStatus" if ($code == 3);
319		return "DSIOpenSession" if ($code == 4);
320		return "DSITickle" if ($code == 5);
321		return "DSIWrite" if ($code == 6);
322		return "DSIAttention" if ($code == 7);
323	}
324	return "[$what/$code] unknown";
325}
326
327
328# return "counted" data at @packet[$offset]
329# when called with a zero as the first argument, this will
330# look in the packet for the count.  Otherwise, it will
331# assume I know what I'm doing.  (hah, what a foolish function..)
332
333sub extract
334{
335	my ($count, $offset, @packet) = @_;
336	my ($i, $j);
337	my (@items, $data);
338	my ($hack);
339
340	if ($count == 0)
341	{
342		($count) = unpack("C", @packet[$offset]);
343		return if ($count == 0);
344		$offset++;
345	}
346	else
347	{
348		$hack = 1;
349	}
350	#print ">> extracting $count items from offset $offset\n";
351	for ($i=0;$i<$count;$i++)
352	{
353		#print "Working on count $i\n";
354		my ($len) = unpack("C1", @packet[$offset]);
355		$data = join("",  @packet[$offset+1..$offset+$len]);
356		#print "$i. [$data] ($len)\n";
357		push (@items, $data);
358		$offset = $offset + $len + 1;
359		#print "new offset is $offset\n";
360	}
361	return $data if ($hack);
362	return @items;
363}
364
365sub draw_icon
366{
367	my ($offset, @packet) = @_;
368	my ($cols);
369	my ($i, $j);
370
371	# icons are 32x32 bitmaps; 128 byte icon + 128 byte mask
372	# to show the mask, change 128 to 256.
373
374	for ($i=0;$i<128;$i++)
375	{
376		my ($c) = @packet[$i+$offset];
377		my ($bin) = unpack ("B*", $c);
378
379		for ($j=0;$j<8;$j++)
380		{
381			if (substr($bin, $j, 1))
382			{
383				print "#";
384			}
385			else
386			{
387				print " ";
388			}
389		}
390		$cols++;
391		if ($cols == 4)
392		{
393			$cols = 0;
394			print "\n";
395		}
396
397	}
398	print "\n";
399}
400
401
402sub parse_afp_flags
403{
404	my ($flags) = shift @_;
405	my (@flags);
406
407	# $flags is a 16 bit little-endian number
408
409	push (@flags, "SupportsCopyFile") if ($flags & (1<<0));
410	push (@flags, "SupportsChgPwd") if ($flags & (1<<1));
411	push (@flags, "DontAllowSavePwd") if ($flags & (1<<2));
412	push (@flags, "SupportsServerMessages") if ($flags & (1<<3));
413	push (@flags, "SupportsServerSignature") if ($flags & (1<<4));
414	push (@flags, "SupportsTCP/IP") if ($flags & (1<<5));
415	push (@flags, "SupportsSrvrNotifications") if ($flags & (1<<6));
416	push (@flags, "SupportsReconnect") if ($flags & (1<<7));
417	push (@flags, "SupportsOpenDirectory") if ($flags & (1<<8));
418	push (@flags, "SupportsUTF8Servername") if ($flags & (1<<9));
419	push (@flags, "SupportsUUIDs") if ($flags & (1<<10));
420	push (@flags, "SupportsSuperClient") if ($flags & (1<<15));
421
422	return @flags;
423}
424
425
426sub hexdump
427{
428	my ($buf) = @_;
429	my ($p, $c, $pc, $str);
430	my ($i);
431
432	for ($i=0;$i<length($buf);$i++)
433 	{
434 		$p = substr($buf, $i, 1);
435		$c = ord ($p);
436		printf "%.2x ", $c;
437		$pc++;
438		if (($c > 31) && ($c < 127))
439		{
440			$str .= $p;
441		}
442		else
443		{
444			$str .= ".";
445		}
446		if ($pc == 16)
447		{
448			print " $str\n";
449			undef $str;
450			$pc = 0;
451		}
452 	}
453	print "   " x (16 - $pc);
454 	print " $str \n";
455}
456
457
458sub extract_network_address
459{
460	my ($offset, @packet) = @_;
461	my ($count);
462	my ($i) = 0;
463	my ($data);
464
465	# get the number of addresses
466        ($count) = unpack("C", @packet[$offset]);
467        return if ($count == 0);
468        $offset++;
469
470        #print "\n>> extracting $count items from offset $offset\n";
471        for ($i=0;$i<$count;$i++)
472        {
473                #print "Working on count $i\n";
474                my ($len) = unpack("C1", @packet[$offset]);
475		#printf "len:  %u\n",$len;
476		my ($type) = unpack("C1", @packet[$offset+1]);
477		#printf "type: %u\n",$type;
478                $data = join("",  @packet[$offset+2..$offset+$len-1]);
479                #print "$i. [$data] ($len)\n";
480                $offset = $offset + $len ;
481                #print "new offset is $offset\n";
482
483
484		# 1st byte is 'tag'
485		# 1 - IP address; 4 bytes
486		# 2 - IP address (4) + port (2)
487		# 3 - DDP (2 bytes net, 1 byte node, 1 byte socket)
488		# 4 - DNS address
489		# 5 - IP address (4) + port (2), for SSH tunnel
490		# 6 - IPV6 address (16)
491		# 7 - IPV6 address (16) + port (2)
492
493		my (@nap) = unpack("C*", $data);
494
495		if ($type == 1)
496		{
497			# quad
498			my ($ip) = sprintf "%d.%d.%d.%d (TCP/IP address)",
499				$nap[0], $nap[1], @nap[2], @nap[3];
500
501			print "Network address: $ip\n";
502		}
503		elsif ($type == 2)
504		{
505			# quad+port
506			my ($ipport) = sprintf "%d.%d.%d.%d:%d",
507				@nap[0], @nap[1], @nap[2], @nap[3], (@nap[4]*256 + @nap[5]);
508			print "Network address: $ipport (TCP/IP address and port)\n";
509		}
510		elsif ($type == 3)
511		{
512			printf "Network address: %d.%d (ddp address)\n",
513				(@nap[0] * 256) + @nap[1], @nap[2];
514		}
515		elsif ($type == 4)
516		{
517			print "Network address: $data (DNS address)\n";
518		}
519		elsif ($type == 5)
520		{
521			# according to the specs this should be an IP address
522			# however, OSX Server uses the FQDN instead
523			print "Network address: $data (SSH tunnel address)\n";
524		}
525		elsif ($type == 6 || $type == 7)
526		{
527			print "Network address: ";
528			my ($j) = 0;
529			for ( $j = 0; $j<=13; $j = $j+2) {
530				printf("%.2x%.2x:", @nap[$j], @nap[$j+1]);
531			}
532			printf("%.2x%.2x", @nap[14], @nap[15]);
533			if ($type == 7 ) {
534				printf(":%d", (@nap[16]*256) + @nap[17]);
535				print " (IPv6 address + port)\n";
536			}
537			else {
538				print " (IPv6 address)\n";
539			}
540		}
541		else
542		{
543			printf "unsupported address type: %u\n", $type;
544		}
545
546        }
547}
548