18. Internet Services

Simple DNS Lookups

//----------------------------------------------------------------------------------
name = 'www.perl.com'
addresses = InetAddress.getAllByName(name)
println addresses // => {www.perl.com/208.201.239.36, www.perl.com/208.201.239.37}
// or to just resolve one:
println InetAddress.getByName(name) // => www.perl.com/208.201.239.36
// try a different address
name = 'groovy.codehaus.org'
addresses = InetAddress.getAllByName(name)
println addresses // => {groovy.codehaus.org/63.246.7.187}
// starting with IP address
address = InetAddress.getByAddress([208, 201, 239, 36] as byte[])
println address.hostName // => www.oreillynet.com

// For more complex operations use dnsjava: http://www.dnsjava.org/
import org.xbill.DNS.*
System.setProperty("sun.net.spi.nameservice.provider.1","dns,dnsjava")
Lookup lookup = new Lookup('cnn.com', Type.ANY)
records = lookup.run()
println "${records?.size()} record(s) found"
records.each{ println it }
// =>
// 17 record(s) found
// cnn.com.     55  IN  A   64.236.16.20
// cnn.com.     55  IN  A   64.236.16.52
// cnn.com.     55  IN  A   64.236.16.84
// cnn.com.     55  IN  A   64.236.16.116
// cnn.com.     55  IN  A   64.236.24.12
// cnn.com.     55  IN  A   64.236.24.20
// cnn.com.     55  IN  A   64.236.24.28
// cnn.com.     55  IN  A   64.236.29.120
// cnn.com.     324 IN  NS  twdns-02.ns.aol.com.
// cnn.com.     324 IN  NS  twdns-03.ns.aol.com.
// cnn.com.     324 IN  NS  twdns-04.ns.aol.com.
// cnn.com.     324 IN  NS  twdns-01.ns.aol.com.
// cnn.com.     3324    IN  SOA twdns-01.ns.aol.com. hostmaster.tbsnames.turner.com. 2007011203 900 300 604801 900
// cnn.com.     3324    IN  MX  10 atlmail3.turner.com.
// cnn.com.     3324    IN  MX  10 atlmail5.turner.com.
// cnn.com.     3324    IN  MX  20 nycmail2.turner.com.
// cnn.com.     3324    IN  MX  30 nycmail1.turner.com.

// faster reverse lookup using dnsjava
def reverseDns(hostIp) {
    name = ReverseMap.fromAddress(hostIp)
    rec = Record.newRecord(name, Type.PTR, DClass.IN)
    query = Message.newQuery(rec)
    response = new ExtendedResolver().send(query)
    answers = response.getSectionArray(Section.ANSWER)
    if (answers) return answers[0].rdataToString() else return hostIp
}
println '208.201.239.36 => ' + reverseDns('208.201.239.36')
// => 208.201.239.36 => www.oreillynet.com.

def hostAddrs(name) {
    addresses = Address.getAllByName(name)
    println addresses[0].canonicalHostName + ' => ' + addresses.collect{ it.hostAddress }.join(' ')
}
hostAddrs('www.ora.com')
// => www.oreillynet.com. => 208.201.239.36 208.201.239.37
hostAddrs('www.whitehouse.gov')
// => 61.9.209.153 => 61.9.209.153 61.9.209.151
//----------------------------------------------------------------------------------

Being an FTP Client

//----------------------------------------------------------------------------------
// commons net examples (explicit error handling not shown)
import java.text.DateFormat
import org.apache.commons.net.ftp.FTPClient
// connect
server = "localhost"                   //server = "ftp.host.com"

ftp = new FTPClient()
ftp.connect( server )
ftp.login( 'anonymous', 'guest' )     //ftp.login( 'username', 'password' )

println "Connected to $server. $ftp.replyString"

// retrieve file
ftp.changeWorkingDirectory( '.' )  //ftp.changeWorkingDirectory( 'serverFolder' )
file = new File('README.txt') //new File('localFolder' + File.separator + 'localFilename')

file.withOutputStream{ os ->
    ftp.retrieveFile( 'README.txt', os )  //ftp.retrieveFile( 'serverFilename', os )
}

// upload file
file = new File('otherFile.txt') //new File('localFolder' + File.separator + 'localFilename')
file.withInputStream{ fis -> ftp.storeFile( 'otherFile.txt', fis ) }

// List the files in the directory
files = ftp.listFiles()
println "Number of files in dir: $files.length"
df = DateFormat.getDateInstance( DateFormat.SHORT )
files.each{ file ->
    println "${df.format(file.timestamp.time)}\t $file.name"
}

// Logout from the FTP Server and disconnect
ftp.logout()
ftp.disconnect()
// =>
// Connected to localhost. 230 User logged in, proceed.
// Number of files in dir: 2
// 18/01/07  otherFile.txt
// 25/04/06  README.txt


// Using AntBuilder; for more details, see:
// http://ant.apache.org/manual/OptionalTasks/ftp.html
ant = new AntBuilder()
ant.ftp(action:'send', server:'ftp.hypothetical.india.org', port:'2121',
        remotedir:'/pub/incoming', userid:'coder', password:'java1',
        depends:'yes', binary:'no', systemTypeKey:'Windows',
        serverTimeZoneConfig:'India/Calcutta'){
    fileset(dir:'htdocs/manual'){
        include(name:'**/*.html')
    }
}
//----------------------------------------------------------------------------------

Sending Mail

//----------------------------------------------------------------------------------
// using AntBuilder; for more info, see:
// http://ant.apache.org/manual/CoreTasks/mail.html
ant = new AntBuilder()
ant.mail(mailhost:'smtp.myisp.com', mailport:'1025', subject:'Test build'){
  from(address:'config@myisp.com')
  replyto(address:'me@myisp.com')
  to(address:'all@xyz.com')
  message("The ${buildname} nightly build has completed")
  attachments(){ // ant 1.7 uses files attribute in earlier versions
    fileset(dir:'dist'){
      include(name:'**/*.zip')
    }
  }
}

// using commons net
import org.apache.commons.net.smtp.*
client = new SMTPClient()
client.connect( "mail.myserver.com", 25 )
if( !SMTPReply.isPositiveCompletion(client.replyCode) ) {
    client.disconnect()
    System.err.println("SMTP server refused connection.")
    System.exit(1)
}

// Login
client.login( "myserver.com" )

// Set the sender and recipient(s)
client.setSender( "config@myisp.com" )
client.addRecipient( "all@xyz.com" )

// Use the SimpleSMTPHeader class to build the header
writer = new PrintWriter( client.sendMessageData() )
header = new SimpleSMTPHeader( "config@myisp.com", "all@xyz.com", "My Subject")
header.addCC( "me@myisp.com" )
header.addHeaderField( "Organization", "My Company" )

// Write the header to the SMTP Server
writer.write( header.toString() )

// Write the body of the message
writer.write( "This is a test..." )

// Close the writer 
writer.close()
if ( !client.completePendingCommand() ) // failure
    System.exit( 1 )

// Logout from the e-mail server (QUIT) and close connection
client.logout()
client.disconnect()

// You can also use JavaMail; for more details, see:
// http://java.sun.com/products/javamail/

// For testing programs which send emails, consider:
// Dumbster (http://quintanasoft.com/dumbster/)
//----------------------------------------------------------------------------------

Reading and Posting Usenet News Messages

//----------------------------------------------------------------------------------
// slight variation to original cookbook:
// prints 1st, 2nd and last articles from random newsgroup
import org.apache.commons.net.nntp.NNTPClient
postingPerm = ['Unknown', 'Moderated', 'Permitted', 'Prohibited']
client = new NNTPClient()
client.connect("news.example.com")
list = client.listNewsgroups()
println "Found ${list.size()} newsgroups"
aList = list[new Random().nextInt(list.size())]
println "$aList.newsgroup has $aList.articleCount articles"
println "PostingPermission = ${postingPerm[aList.postingPermission]}"
first = aList.firstArticle
println "First=$first, Last=$aList.lastArticle"
client.retrieveArticle(first)?.eachLine{ println it }
client.selectNextArticle()
client.retrieveArticle()?.eachLine{ println it }
client.retrieveArticle(aList.lastArticle)?.eachLine{ println it }
writer = client.postArticle()
// ... use writer ...
writer.close()
client.logout()
if (client.isConnected()) client.disconnect()
// =>
// Found 37025 newsgroups
// alt.comp.sys.palmtops.pilot has 730 articles
// PostingPermission = Permitted
// First=21904, Last=22633
// ...
//----------------------------------------------------------------------------------

Reading Mail with POP3

//----------------------------------------------------------------------------------
// slight variation to original cookbook to print summary of messages on server
// uses commons net
import org.apache.commons.net.pop3.POP3Client
server = 'pop.myisp.com'
username = 'gnat'
password = 'S33kr1T Pa55w0rD'
timeoutMillis = 30000

def printMessageInfo(reader, id) {
    def from, subject
    reader.eachLine{ line ->
        lower = line.toLowerCase()
        if (lower.startsWith("from: ")) from = line[6..-1].trim()
        else if (lower.startsWith("subject: ")) subject = line[9..-1].trim()
    }
    println "$id From: $from, Subject: $subject"
}

pop3 = new POP3Client()
pop3.setDefaultTimeout(timeoutMillis)
pop3.connect(server)

if (!pop3.login(username, password)) {
    System.err.println("Could not login to server.  Check password.")
    pop3.disconnect()
    System.exit(1)
}
messages = pop3.listMessages()
if (!messages) System.err.println("Could not retrieve message list.")
else if (messages.length == 0) println("No messages")
else {
    messages.each{ message ->
        reader = pop3.retrieveMessageTop(message.number, 0)
        if (!reader) {
            System.err.println("Could not retrieve message header. Skipping...")
        }
        printMessageInfo(new BufferedReader(reader), message.number)
    }
}

pop3.logout()
pop3.disconnect()

// You can also use JavaMail; for more details, see:
// http://java.sun.com/products/javamail/
//----------------------------------------------------------------------------------

Simulating Telnet from a Program

//----------------------------------------------------------------------------------
// Variation to original cookbook: this more extensive example
// uses telnet to extract weather information about Sydney from
// a telnet-based weather server at the University of Michigan.
import org.apache.commons.net.telnet.TelnetClient

def readUntil( pattern ) {
    sb = new StringBuffer()
    while ((ch = reader.read()) != -1) {
        sb << (char) ch
        if (sb.toString().endsWith(pattern)) {
            def found = sb.toString()
            sb = new StringBuffer()
            return found
        }
    }
    return null
}

telnet = new TelnetClient()
telnet.connect( 'rainmaker.wunderground.com', 3000 )
reader = telnet.inputStream.newReader()
writer = new PrintWriter(new OutputStreamWriter(telnet.outputStream),true)
readUntil( "Welcome" )
println 'Welcome' + readUntil( "!" )
readUntil( "continue:" )
writer.println()
readUntil( "-- " )
writer.println()
readUntil( "Selection:" )
writer.println("10")
readUntil( "Selection:" )
writer.println("3")
x = readUntil( "Return" )
while (!x.contains('SYDNEY')) {
    writer.println()
    x = readUntil( "Return" )
}
m = (x =~ /(?sm).*(SYDNEY.*?)$/)
telnet.disconnect()
println m[0][1]
// =>
// Welcome to THE WEATHER UNDERGROUND telnet service!
// SYDNEY           FAIR      10AM   81  27
//----------------------------------------------------------------------------------

Pinging a Machine

//----------------------------------------------------------------------------------
address = InetAddress.getByName("web.mit.edu")
timeoutMillis = 3000
println address.isReachable(timeoutMillis)
// => true (if firewalls don't get in the way, may require privileges on Linux,
//          may not use ICMP but rather Echo protocol on Windows machines)

// You can also use commons net EchoUDPClient and EchoTCPClient to interact
// with the Echo protocol - sometimes useful for ping-like functionality.
//----------------------------------------------------------------------------------

Using Whois to Retrieve Information from the InterNIC

//----------------------------------------------------------------------------------
import org.apache.commons.net.WhoisClient
whois = new WhoisClient()
whois.connect(WhoisClient.DEFAULT_HOST)
result = whois.query('cnn.com') // as text of complete query
println result // could extract info from result here (using e.g. regex)
whois.disconnect()
//----------------------------------------------------------------------------------

Program: expn and vrfy

//----------------------------------------------------------------------------------
// not exact equivalent to original cookbook: just shows raw functionality
client = new SMTPClient()
client.connect( "smtp.example.com", 25 )
println client.verify("george") // => true
println client.replyString // => 250 George Washington <george@wash.dc.gov>
println client.verify("jetson") // => false
println client.replyString // => 550 jetson... User unknown
client.expn("presidents")
println client.replyString
// =>
// 250-George Washington <george@wash.dc.gov>
// 250-Thomas Jefferson <tj@wash.dc.gov>
// 250-Ben Franklin <ben@here.us.edu>
// ...

// expect these commands to be disabled by most public servers due to spam
println client.replyString
// => 502 Command is locally disabled
//----------------------------------------------------------------------------------