8

I try to write Swift snippets to send commands to Telnet but I need to log in first.

On OSX I generated a Telnet service: launchctl load -F /System/Library/LaunchDaemons/telnet.plist. After that I able to connect from other computers to IP: XXX.XXX.X.XXX port 23.

For example from Windows 7:

telnet XXX.XXX.X.XXX 23
.....
Darwin/BSD (fess.local) (ttys009)

login: snaggs
Password:xxxxxx

snaggs:~ lur$

In the command line, Telnet asks me for the user and password. After that I can run any Linux commands. So far so good.

I wrote Telnet module for Android so OSX Telnet service works properly. This means that I see login: and password: outputs so I am able to handle it.

Now I'm stuck with iOS Swift, I Googled and found pretty good solution that should work in my case:

So I wrote my home work:

class TelnetClient : NSObject, NSStreamDelegate{

    private var inputStream: NSInputStream!
    private var outputStream: NSOutputStream!


func initNetworkCommunication(){
        let host : CFString = "XXX.XXX.X.XXX"
        let port : UInt32 = 23
        var readstream : Unmanaged<CFReadStream>?
        var writestream : Unmanaged<CFWriteStream>?

        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readstream, &writestream)

 //        var _inputStream: NSInputStream? 
 //        var _outputStream: NSOutputStream? 
 //        NSStream.getStreamsToHostWithName("XXX.XXX.X.XXX", port: 23, inputStream: &_inputStream, outputStream: &_outputStream) 
 //        self.inputStream = _inputStream! 
 //        self.outputStream = _outputStream!


        self.inputStream = readstream!.takeRetainedValue()
        self.outputStream = writestream!.takeRetainedValue()

        self.inputStream.delegate = self
        self.outputStream.delegate = self


        self.inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        self.outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

        self.inputStream.open()
        self.outputStream.open()

    }


func sendMessage(message:String){

    let data: NSData = message.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)! //NSUTF8StringEncoding
    let stream: NSInputStream = NSInputStream(data: data)

        var buffer = [UInt8](count: 8, repeatedValue: 0)
        stream.open()

        if stream.hasBytesAvailable {
          let result :Int = stream.read(&buffer, maxLength: buffer.count)
        }
        self.outputStream.write(&buffer, maxLength: buffer.count)

        println("wrote to Server: \(message)")
    }

}

Stream callback override method from NSStreamDelegate:

func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
        switch (eventCode){
        case NSStreamEvent.ErrorOccurred:
            NSLog("ErrorOccurred")
            break
        case NSStreamEvent.EndEncountered:
            NSLog("EndEncountered")
            break
        case NSStreamEvent.None:
            NSLog("None")
            break
        case NSStreamEvent.HasBytesAvailable:
            NSLog("HasBytesAvaible")
            // ... NEVER ENTERS HERE
            var buffer = [UInt8](count: 4096, repeatedValue: 0)
        if ( aStream == self.inputStream){

            while (self.inputStream.hasBytesAvailable){
                var len = self.inputStream.read(&buffer, maxLength: buffer.count)
                if(len > 0){
                    var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
                    if (output != ""){
                        NSLog("server said: %@", output!)
                    }
                }
            }
        }
            break
        //case NSStreamEvent.allZeros:
        //    NSLog("allZeros")
        //     break
        case NSStreamEvent.OpenCompleted:
            NSLog("OpenCompleted") // <-- called on initialisation
            break
        case NSStreamEvent.HasSpaceAvailable:
            NSLog("HasSpaceAvailable")
            break
        default:
             NSLog("default")
             break

        }
    }

First of all, nothing happens when I try to call initNetworkCommunication(). I expect calling of func stream or I'm wrong?. It should be NSStreamEvent.HasBytesAvailable state or I'm wrong?

When I call sendMessage("snags"), I get response in stream that falls on case NSStreamEvent.HasSpaceAvailable. What does it mean?


My expected results: on connect I should get login: output but get nothing.

Community
  • 1
  • 1
snaggs
  • 5,543
  • 17
  • 64
  • 127
  • @Brian why did you remove `Swift iOS` from title? this question refers to iOS Swift and not precisely telnet – snaggs Sep 15 '15 at 21:07
  • Including the tags in the title is redundant and creates clutter. The tags will make sure that it's obvious what the topics are. You can read more in [this meta question](http://meta.stackexchange.com/questions/19190/should-questions-include-tags-in-their-titles). – Brian Sep 15 '15 at 21:11
  • NSStream will give you a lot of headache, I really recommend using third-party framework like [CocoaAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket) by Robbie Hanson , All you have to do is include `GCDAsyncSocket.h` in your `bridge-header.h` – Brian Nezhad Sep 17 '15 at 15:40
  • Also make sure you use **return** of you server type correctly, `(\r\n` , `\r `, `\n` etc.) – Brian Nezhad Sep 17 '15 at 15:42
  • @LGL ok, ill try it out – snaggs Sep 21 '15 at 13:56
  • @snaggs K, let me know if you need help. :) – Brian Nezhad Sep 21 '15 at 15:06

1 Answers1

-1

You are almost done about NSStreamDelegate, but you need to know 'rfc 854 telnet protocol specification'.

Preceding communication there needs an telnet negotiation between host and your code.

In your call back's 'NSStreamEvent.HasBytesAvailable' case, dump your buffer, converting to NSData, like

NSLog( "%@", NSData(bytes: buffer, length: len) )

you will get

<fffd18ff fd20fffd 23fffd27 fffd24>

which is telnet negotiation that you must respond according to the specification.

2nd question.

NSStreamEvent.HasSpaceAvailable means that 'A boolean value that indicates whether the receiver can be written to.'

Before you send message via NSOutputStream, you must check hasSpaceAvailable property of NSOutputStream in you 'sendMessage' method.

Or use queue for data to be sent and send it when fall on NSStreamEvent.HasBytesAvailable, it's my recommendation.

See this answer. How to use NSInputStream and NSOutputStream

Community
  • 1
  • 1
Satachito
  • 5,838
  • 36
  • 43
  • On connect i get `NSStreamEvent.OpenCompleted` case, after send request, i get `NSStreamEvent.HasSpaceAvailable` but never `NSStreamEvent.HasBytesAvailable` – snaggs Sep 18 '15 at 14:50
  • Nothing happens when i run `self.outputStream.write(&buffer, maxLength: buffer.count)` – snaggs Sep 18 '15 at 14:51