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