Trying to send telnet commands to a server - ios

I'm trying to connect to a telnet server (running NetLinx by AMX, in case it matters) and send it a few commands as if I opened Terminal and did "telnet (address)" and started typing commands. I can receive messages but can't send them using this code I found from a tutorial at http://www.raywenderlich.com/3932/how-to-create-a-socket-based-iphone-app-and-server:
- (void)sendMessage: (NSString*) message{ //called when the user interacts with UISwitches, is supposed to send message to server
NSData *data = [[NSData alloc] initWithData:[message dataUsingEncoding:NSASCIIStringEncoding]]; //is ASCIIStringEncoding what I want?
[outputStream write:[data bytes] maxLength:[data length]];
NSLog(#"Sent message to server: %#", message);
}
- (void)initNetworkCommunication { //called in viewDidLoad of the view controller
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.1.90", 23, &readStream, &writeStream); //192.168.1.90 is the server address, 23 is standard telnet port
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
It appears to send some kind of message to the server but maybe in the wrong format. My guess is that it's a string formatting problem. Is ASCII what I want to use for telnet? If I take the message it prints when it sends a command and paste that right into Terminal with telnet running, the server receives and processes the command normally.
Here is an example command that I'm trying to send: SEND_STRING dvJAND,"'#AUX2=0',$0D"
Another mystery is that, reading the input stream and printing, I see this when the above is sent:
2013-08-20 00:20:23.791 [7548:907] server said: S
2013-08-20 00:20:23.793 [7548:907] server said: END_STRING dvJAND,"'#AUX
2013-08-20 00:20:23.795 [7548:907] server said: 2=0',$0D"
But when I type that command in Terminal, the server does not respond at all and does its job as it should.

Found out that the only thing I was doing wrong was not ending my messages with \r! I read that you need to end with \n, but \r is what did it for me.

Related

Difference between Apple TLS with Objective-C and Swift

I am using Apple's CFNetworking to get a TLS stream. I'm having a bit of trouble porting over the Objective-C code to Swift.
With the exact same steps, it works when using Objective-C, but the handshake consistently fails when attempting with Swift.
Working Obj-c
- (void)connect()
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(__bridge CFStringRef)hostAddress,
port,
&readStream,
&writeStream);
self.inputStream = (__bridge_transfer NSInputStream *)readStream;
self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey];
[self.outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey];
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
}
Non-working Swift
func connect() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
let host = "some_host"
let hostAsCFString = host as NSString
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
hostAsCFString,
1337,
&readStream,
&writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
inputStream!.delegate = self
outputStream!.delegate = self
inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL,
forKey: NSStreamSocketSecurityLevelKey)
outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL,
forKey: NSStreamSocketSecurityLevelKey)
inputStream!.scheduleInRunLoop(.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream!.scheduleInRunLoop(.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputStream!.open()
outputStream!.open()
}
Both are attempting to connect to the same server and same port.
Screen grabs of wireshark:
Working Obj-C
Non-working Swift
I'm pretty clueless as to what is going on. I have no idea why the Obj-C version starts a Client Hello with TLS v1.2, but Swift tries with TLS v1.0, then just gives up. No idea why the Swift version takes so long to send the Client Hello, a Keepalive packet is sent prior? Any help would be greatly appreciated.
Turns out there was no difference, I was just an idiot. I was immediately calling outputStream.write() once the NSStreamEvent.OpenCompleted event had be called for both input and output streams. Which was writing into the buffer of the SSL Handshake, and messing it all up.
Not found until I created a MVP for both Obj-c and Swift, which just goes to show that if you take the time to create a valid MVP, you'll probably figure it out while writing it. Now, if I can only find a way to be notified once the handshake is completed, this problem will always be avoided.

iOS:Unable to send message to socket server using NSOutputStream connection drops when i try to send message

I have used following code to connect to the server
- (void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,(CFStringRef)URLBASE, 8080, &readStream, &writeStream);
inputStream = (__bridge_transfer NSInputStream *)readStream;
outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// open input and output stream of socket to send and receive data
[inputStream open];
[outputStream open];
}
and following code to send message using output stream
-(IBAction)sendMessage{
if (outputStream.hasSpaceAvailable) {
[outputStream write:[data bytes] maxLength:[data length]];
}
}
but when I try to send message the connection drops and gives connection reset by remote peer.
when I try to send message the connection drops and gives connection
reset by remote peer.
The error text unequivocally states that the peer dropped the connection, so you have to look there for the reason rather than in the code above.

How to close NSStream input/output programmatically

I have an application that connects users to a serve. Users can connect to a host to communicate inserting an IP address and communicate using NSStream. Pressing the connect button starts a connection to the host and the user can receive and send messages.
I want to give a user the ability to disconnect from the server to not listen to pressing a disconnect button. The problem is that I am never able to successfully close the stream.
This is my current implementation:
// Create connection "Connect Button"
- (void)initNetworkCommunication:(UIViewController *)sender {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"123.456.789.123", 80, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
controller = sender;
}
// Stop connection "Disconnect Button"
- (void)stopNetworkConnection {
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream release];
[outputStream release];
inputStream = nil;
outputStream = nil;
NSLog(#"Disconnect from stream");
}
The flow of operations is as follows:
User insert a valid IP address;
User press "Connect Button";
Everything works fine, connection established and user can send/receive messages;
User press "Disconnect Button" and user do not receive messages;
User press "Connect Button" but no longer able to connect
I tried this solution but it did not work:
NSStream closing and opening error
Thanks in advance for the help

Sending C++ Protocol Buffer Messages from iOS

In a previous Stack Overflow question, people were super helpful in showing me the error of my ways while building my Akka socket server and I actually now have an Akka socket client that can send messages with the following framing:
message length: 4 bytes
message type: 4 bytes
message payload: (length) bytes
Here's the iOS code that I'm using to send the message:
NSInputStream *inputStream;
NSOutputStream *outputStream;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"localhost", 9999, &readStream, &writeStream);
inputStream = (__bridge_transfer NSInputStream *)readStream;
outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// [inputStream open];
[outputStream open];
NSLog(#"NSData raw zombie is %d bytes.", [rawZombie length]);
uint32_t length = (uint32_t)htonl([rawZombie length]);
uint32_t messageType = (uint32_t)htonl(1);
NSLog(#"Protobuf byte size %d", zombieSighting->ByteSize());
[outputStream write:(uint8_t *)&length maxLength:4];
[outputStream write:(uint8_t *)&messageType maxLength:4];
[outputStream write:(uint8_t *)[rawZombie bytes] maxLength:length];
[outputStream close];
The 'rawZombie' variable (NSData *) comes from the following method:
- (NSData *)getDataForZombie:(kotancode::ZombieSighting *)zombie {
std::string ps = zombie->SerializeAsString();
NSLog(#"raw zombie string:\n[%s]", ps.c_str());
return [NSData dataWithBytes:ps.c_str() length:ps.size()];
}
The symptom I'm seeing is that I receive the message, sent by iOS, and it's length is correct, as is the message type (1), and the body comes across fine. The Akka server using the Scala protobufs de-serializes the message and prints out all of the values on the message perfectly. The problem is, immediately after I get that message, the Akka server thinks it got another message (more data came in on the stream, apparently). The data at the end is not the same each time I run the iOS app.
For example, here's some trace output from two consecutive message receives:
received message of length 45 and type 1
name:Kevin
lat: 41.007
long: 21.007
desc:This is a zombie
zombie type:FAST
received message of length 7 and type 4
received message of length 45 and type 1
name:Kevin
lat: 41.007
long: 21.007
desc:This is a zombie
zombie type:FAST
received message of length 164 and type 1544487554
So you can see that right after the Akka server receives the right data for the message, it also receives some random arbitrary crap. Given that I have the Akka client working properly without this extra arbitrary crap, I am assuming that there's something wrong with how I am writing the protobuf object to the NSStream... Can anybody spot my stupid mistake, as I'm sure that's what is happening here.
Bloody hell. I can't believe I didn't see this. In the line of code here:
[outputStream write:(uint8_t *)[rawZombie bytes] maxLength:length]
I am using the value "length" for the maximum number of bytes to transmit. Unfortunately, that value has already had it's "endian" order flipped in preparation for transmission across the network. I replaced "length" with [rawZombie length] and it worked like a charm.
:(

VoIP socket on iOS - no notifications received

I have a VoIP app that uses a TCP service to wake it up on incoming calls.
The TCP socket is created with this code fragment:
CFReadStreamRef read = NULL;
CFWriteStreamRef write = NULL;
...
CFStreamCreatePairWithSocketToHost(NULL,(__bridge CFStringRef)shost, port, &read, &write);
self.read = (__bridge NSInputStream*)read;
self.write = (__bridge NSOutputStream*)write;
if (![self.read setProperty:NSStreamNetworkServiceTypeVoIP
forKey:NSStreamNetworkServiceType]){
[Log log:#"Could not set VoIP mode to read stream"];
}
if (![self.write setProperty:NSStreamNetworkServiceTypeVoIP
forKey:NSStreamNetworkServiceType]){
[Log log:#"Could not set VoIP mode to write stream"];
}
self.read.delegate = self;
self.write.delegate = self;
CFRelease(read);
CFRelease(write);
[self.read scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.write scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.read open];
[self.write open];
I've also set the following:
VoIP & Audio in the info plist
Keep alive timer using [UIApplication sharedApplication] setKeepAliveTimeout
UIRequiresPersistentWiFi = YES in the info plist (quite sure it's not required, but...)
This works well while the app is in the foreground, and even works well in the background for a few minutes, but after a few minutes - the app does not receive any new TCP messages.
It doesn't work on wifi or 3G, same result for both.
I also tried setting the property just to the read stream (though the read and write point to the same socket).
Whenever I receive data on the TCP or send data I also start a short background task.
BTW - everything takes place on the main thread.
I've checked if the app crashes - it doesn't.
The same behavior can be observed while debugging on the device - after a while - nothing is received (no crashes, warnings, anything).
What am I doing wrong?
Looks like your code should work. But there may be two technical problems I can think of:
If you try this from LAN connection, while app in background the LAN router can close passive TCP connection because, in this case, SIP stack(guess you use SIP protocol) can't send data keep alive every 15 to 30 secs like it would in foreground.
Less likely, suppose you know what you doing, but since registration keep alive can be triggered only once in 10 minutes while in background, make sure that SIP server allows such a long expire period and you define it right in registration message.
Try the following code.Make sure you have only one voip socket in your app.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"1.2.3.4",9999, &readStream, &writeStream);
CFReadStreamSetProperty(readStream,kCFStreamNetworkServiceType,kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
inputStream = (NSInputStream *)readStream;
[inputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
outputStream = (NSOutputStream *)writeStream;
[outputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
In ViewController.h file add
#property (nonatomic, strong) NSInputStream *inputStream;
#property (nonatomic, strong) NSOutputStream *outputStream;
#property (nonatomic) BOOL sentPing;
In ViewController.m file add after #implementation ViewController
const uint8_t pingString[] = "ping\n";
const uint8_t pongString[] = "pong\n";
Add following code in viewDidLoad
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(#"192.168.0.104"), 10000, &readStream, &writeStream);
//in above line user your MAC IP instead of 192.168.0.104
self.sentPing = NO;
//self.communicationLog = [[NSMutableString alloc] init];
self.inputStream = (__bridge_transfer NSInputStream *)readStream;
self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[self.inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.inputStream open];
[self.outputStream open];
//After every 10 mins this block will be execute to ping server, so connection will be live for more 10 mins
[[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
if (self.outputStream)
{
[self.outputStream write:pingString maxLength:strlen((char*)pingString)];
//[self addEvent:#"Ping sent"];
}
}];
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventNone:
// do nothing.
break;
case NSStreamEventEndEncountered:
//[self addEvent:#"Connection Closed"];
break;
case NSStreamEventErrorOccurred:
//[self addEvent:[NSString stringWithFormat:#"Had error: %#", aStream.streamError]];
break;
case NSStreamEventHasBytesAvailable:
if (aStream == self.inputStream)
{
uint8_t buffer[1024];
NSInteger bytesRead = [self.inputStream read:buffer maxLength:1024];
NSString *stringRead = [[NSString alloc] initWithBytes:buffer length:bytesRead encoding:NSUTF8StringEncoding];
stringRead = [stringRead stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
//[self addEvent:[NSString stringWithFormat:#"Received: %#", stringRead]];
//if server response is 'call' then a notification will go to notification center and it will be fired
//immediately and it will popup if app is in background.
if ([stringRead isEqualToString:#"call"])
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"New VOIP call";
notification.alertAction = #"Answer";
//[self addEvent:#"Notification sent"];
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
//else if ([stringRead isEqualToString:#"ping"])
//{
//if server response is 'ping' then a sting 'pong' will go to server immediately
//[self.outputStream write:pongString maxLength:strlen((char*)pongString)];
//}
}
break;
case NSStreamEventHasSpaceAvailable:
if (aStream == self.outputStream && !self.sentPing)
{
self.sentPing = YES;
if (aStream == self.outputStream)
{
[self.outputStream write:pingString maxLength:strlen((char*)pingString)];
//[self addEvent:#"Ping sent"];
}
}
break;
case NSStreamEventOpenCompleted:
if (aStream == self.inputStream)
{
//[self addEvent:#"Connection Opened"];
}
break;
default:
break;
}
}
Build your app and run
Open terminal in your MAC PC and write nc -l 10000 and press enter
$ nc -l 10000
Then write call and press enter

Resources