local afp = require "afp"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Shows AFP server information. This information includes the server's
hostname, IPv4 and IPv6 addresses, and hardware type (for example
Macmini
or MacBookPro
).
]]
---
-- @output
-- PORT STATE SERVICE
-- 548/tcp open afp
-- | afp-serverinfo:
-- | | Server Flags: 0x8ffb
-- | | Super Client: Yes
-- | | UUIDs: Yes
-- | | UTF8 Server Name: Yes
-- | | Open Directory: Yes
-- | | Reconnect: Yes
-- | | Server Notifications: Yes
-- | | TCP/IP: Yes
-- | | Server Signature: Yes
-- | | ServerMessages: Yes
-- | | Password Saving Prohibited: No
-- | | Password Changing: Yes
-- | |_ Copy File: Yes
-- | Server Name: mac-mini
-- | Machine Type: Macmini2,1
-- | AFP Versions: AFP3.3, AFP3.2, AFP3.1, AFPX03
-- | UAMs: DHCAST128, DHX2, Recon1, Client Krb v2, No User Authent
-- | Server Signature: 000000000000100080000016cbaed4ac
-- | Network Address 1: 192.168.0.190:548
-- | Network Address 2: [fe80:0000:0000:0000:0216:cbff:feae:d4ac]:548
-- | Network Address 3: 192.168.0.190
-- | Directory Name 1: afpserver/LKDC:SHA1.02EBDBCFABF3C222D6FE9FE4D908893568387654@LKDC:SHA1.02EBDBCFABF3C222D6FE9FE4D908893568387654
-- |_ UTF8 Server Name: mac-mini
-- Version 0.2
-- Created 2010/02/09 - v0.1 - created by Andrew Orr
-- Revised 2010/02/10 - v0.2 - added checks for optional fields
author = "Andrew Orr"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
portrule = shortport.portnumber(548, "tcp")
action = function(host, port)
local socket = nmap.new_socket()
local status
local result = {}
local temp
-- set a reasonable timeout value
socket:set_timeout(5000)
-- do some exception handling / cleanup
local catch = function()
socket:close()
end
local try = nmap.new_try(catch)
try( socket:connect(host, port) )
-- get our data
local afp_proto = afp.Proto:new( { socket=socket } )
local response = afp_proto:fp_get_server_info( socket )
response = response.result
-- all the server information is output in the order it occurs in the server
-- response. It might be better rearranged?
-- output the server flags nicely
table.insert(result, string.format("| Server Flags: 0x%04x", response.flags.raw))
table.insert(result, string.format("| Super Client: %s", response.flags.SuperClient and "Yes" or "No"))
table.insert(result, string.format("| UUIDs: %s", response.flags.UUIDs and "Yes" or "No"))
table.insert(result, string.format("| UTF8 Server Name: %s", response.flags.UTF8ServerName and "Yes" or "No"))
table.insert(result, string.format("| Open Directory: %s", response.flags.OpenDirectory and "Yes" or "No"))
table.insert(result, string.format("| Reconnect: %s", response.flags.Reconnect and "Yes" or "No"))
table.insert(result, string.format("| Server Notifications: %s", response.flags.ServerNotifications and "Yes" or "No"))
table.insert(result, string.format("| TCP/IP: %s", response.flags.TCPoverIP and "Yes" or "No"))
table.insert(result, string.format("| Server Signature: %s", response.flags.ServerSignature and "Yes" or "No"))
table.insert(result, string.format("| ServerMessages: %s", response.flags.ServerMessages and "Yes" or "No"))
table.insert(result, string.format("| Password Saving Prohibited: %s", response.flags.NoPasswordSaving and "Yes" or "No"))
table.insert(result, string.format("| Password Changing: %s", response.flags.ChangeablePasswords and "Yes" or "No"))
table.insert(result, string.format("|_ Copy File: %s", response.flags.CopyFile and "Yes" or "No"))
-- other info
table.insert(result, string.format("Server Name: %s", response.server_name))
table.insert(result, string.format("Machine Type: %s", response.machine_type))
-- list the supported AFP versions
temp = "AFP Versions: "
for i = 1, response.afp_version_count-1 do
temp = temp .. response.afp_versions[i] .. ", "
end
temp = temp .. response.afp_versions[response.afp_version_count]
table.insert(result, temp)
-- list the supported UAMs (User Authentication Modules)
temp = "UAMs: "
for i = 1, response.uam_count-1 do
temp = temp .. response.uams[i] .. ", "
end
temp = temp .. response.uams[response.uam_count]
table.insert(result, temp)
-- server signature, not sure of the format here so just showing a hex string
if response.flags.ServerSignature then
table.insert(result, string.format("Server Signature: %s", stdnse.tohex(response.server_signature)))
end
-- listing the network addresses one line each
-- the default for Mac OS X AFP server is to bind everywhere, so this will
-- list all network interfaces that the machine has
for i = 1, response.network_addresses_count do
table.insert(result, string.format("Network Address %d: %s", i, response.network_addresses[i]))
end
-- similar to above
for i = 1, response.directory_names_count do
table.insert(result, string.format("Directory Name %d: %s", i, response.directory_names[i]))
end
-- and finally the utf8 server name
if response.flags.UTF8ServerName then
table.insert(result, string.format("UTF8 Server Name: %s", response.utf8_server_name))
end
return stdnse.format_output(true, result)
end