iOS: Socket Connection Error in sending buffer - ios

We have tried too many diffrent approaches to solve our issue and also searched over net but we only found example of sending string and not the byte array. We require to send byte array through socket connection. Please read issue explanation below.
We require to connect wi-fi device with iOS app. We have successfully connected the device but when we send the command in byte array format, It is returning NSStreamEventHasSpaceAvailable in response. Which is wrong, We require NSStreamEventHasBytesAvailable event.
Below is the code for connection:
-(void) initNetworkCommunication:(NSString*)strHostName {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)strHostName, 2000, &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];
}
When connection is open, we are calling below method to send the command:
- (IBAction) sendCommand {
unsigned char *buffer[5] = {0x3a,0x04,0x01,0x07,0x0c};
NSData *data = [NSData dataWithBytes:buffer length:5];
[outputStream write:[data bytes] maxLength:5];
}
So there is some issue in sendCommand method because we are receiving NSStreamEventHasSpaceAvailable and that is wrong. Because it should be returned NSStreamEventHasBytesAvailable in the response. Can any one please help us how to send byte array {0x3a,0x04,0x01,0x07,0x0c} in iOS so we can receive NSStreamEventHasBytesAvailable event.
According to command manual, When device receives command in correct format then it will return acknowledgement. Below is the manual description.
All commands are 16 bits and are placed in the Data byte 1(MSB) and
Data byte 2(LSB).
The response to a command can either be a specific response relating
to the command or a simple response. The three simple responses are
ACK, NAK and UNK. An ACK signifies that the command was received
successfully. A NAK indicates that there was an error with either the
length byte or an incorrect checksum was calculated. An UNK response
indicates that the recipient does not recognize the command that was
sent.
Value Answer Status
0x06 = ACK (OK)
0x15 = NAK (NOT OK)
0x09 = UNK (Unknown command)
So we should receive any of the above flag(ACK OR NAK OR UNK) but we are receiving NSStreamEventHasSpaceAvailable and this is wrong. Any one please help me to solve my problem.
Thanks in advance.

NSStreamEventHasSpaceAvailable is the correct event to receive after sending data on an output stream - it indicates that you can write at least one byte without blocking (ie. There is space to write data available). NSstreamEventHasBytesAvailable will be signalled against an input stream when data is received on the socket associated with that input stream to indicate that you can issue a read without blocking.
If the device you are sending the data to responds to that data then I would expect you to receive NSStreamEventHasBytesAvailable on inputStream

Related

How can I implement STARTTLS on iOS?

I would like to implement STARTTLS in an iOS application but I'm having trouble figuring out from the documentation how to do it.
So far I have:
I create the socket with CFStreamCreatePairWithSocketToHost and open the streams:
CFStreamCreatePairWithSocketToHost(
NULL, (__bridge CFStringRef)host, port,
&read_stream, &write_stream
);
reader = objc_retainedObject(read_stream);
[ reader setDelegate:self ];
[ reader scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode ];
[ reader open ];
writer = objc_retainedObject(write_stream);
[ writer setDelegate:self ];
[ writer scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode ];
[ writer open ];
I get the right callbacks when data are available on the streams so the connection is working.
I successfully interact with the server in plain text and negociate STARTTLS.
Eventually, the server sends the go-ahead for STARTTLS:
. OK Begin TLS negotiation now.
Now it is time to upgrade the socket form plaintext to TLS. What do I do next?
I thought I should do this as per Apple's documentation:
[ reader setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey ];
[ writer setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey ];
But that doesn't seem to do anything. I'm not surprised that it doesn't work since the documentation says quite clearly:
You must set the property before you open the stream.
Yet of course the stream must be already opened in this case since it is used to conduct the plaintext STARTTLS negociation!
I cannot find any documentation on how to upgrade a socket from plaintext to SSL or perhaps how to layer a new set of SSL-encrypted streams on top of a set of input&output plaintext streams.
I could not find any way to implement STARTTLS using the high-level CFStream API. There is CFStreamCreatePairWithSocket which allows you to connect your own socket and then apply TLS to it afterwards, but there is no way to get the library to verify the remote host name against the certificate host name.
The only way to do it appears to be using this much lower level library: Secure Transport Reference.

NSInputStream for Twitter Streaming API iOS?

Simple question:
Can I use NSInputStream to get data from the streaming API?
If so, can someone outline what this can look like.
I have tried something like
_twitterStream = [[NSInputStream alloc]initWithURL:[NSURL URLWithString:#"https://sitestream.twitter.com/1.1/site.json"]];;
_twitterStream.delegate = self;
[_twitterStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
and adding Delegate Methods, but this is not working. I realize I need to specify what users I need, however, above code is what I have so far.
Thanks!
One approach is using input streams and I do not think you would be able to pull it of easily as connecting to the streaming api requires a couple key http request headers.
CFReadStreamRef readStream;
CFStreamCreatePairWithSocketToHost(nil, (CFStringRef)#"Your IP Here", PORT_NUMBER, &readStream, nil);
self.stream = (__bridge NSInputStream *)(readStream);
[self.stream setDelegate:self];
[self.stream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
Is a typical pattern of connecting to a host server with an input stream.
Another way is to just directly hit the URL like you do.
The problem with these approaches is that they do not address the authentication issues.
The easiest way I found was to use an NSURLSession task with the correct request headers set. Then you start receiving the data packets in the sessionDataDelegate.
Generating the twitter OAuth header can be worse than a prostate exam (trust me on this).
https://dev.twitter.com/oauth/overview/authorizing-requests
Here is a demo project where I hit the stream and dequeue searched for tweets in a tableview.
https://github.com/GregPrice24/TwitterStreamingAPI

Creating sockaddr_in for CocoaAsyncSocket Getting Invalid IPv4 address

I'm having trouble connecting to a Java socket server. I know the server is working because I connect via a Java socket. I'm using the CocoaAsyncSocket library to make a client connection from an iOS device. I've tried the following,
[socket connectToHost:#"XXX.XXX.X.XXX" onPort:9090 error:&err]
method but the server never sees the client connect and the client (CocoaAsyncSocket) thinks its connected. So thats no good, then I realized there was another connection method available.
So I'm thinking I should be using the connectToAddress method instead. I've used this post as a reference for my current code but I'm still getting an error and I'm not sure why. The only difference from my version and the suggested version is for the length they use sa_len and I was getting a error and xCode wanted to switch it to sin_len, so I did. I'm really new to direct socket connections so bear with me.
GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
struct sockaddr_in ip4addr;
ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(9090);
inet_pton(AF_INET, "XXX.XXX.X.XXX", &ip4addr.sin_addr);
NSData *discoveryHost = [NSData dataWithBytes:&ip4addr length:ip4addr.sin_len];
NSError *err = nil;
if (![socket connectToAddress:discoveryHost error:&err])
{
NSLog(#"I CANNOT CONNECT!");
}
else
{
NSLog(#"IM CONNECTED!");
}
The connection fails and the error is,
Error Domain=GCDAsyncSocketErrorDomain Code=2 "A valid IPv4 or IPv6 address was not given" UserInfo=0x8bac880 {NSLocalizedDescription=A valid IPv4 or IPv6 address was not given}
I changed
NSData *discoveryHost = [NSData dataWithBytes:&ip4addr length:ip4addr.sin_len];
to
NSData *discoveryHost = [NSData dataWithBytes:&ip4addr length:sizeof(ip4addr)];
and it fixed that error I was getting. However, the reason I wasn't able to connect via the connectToHost method was due to my server socket code. I have two server sockets accepting connections. I commented out the second and it worked just fine. I'm guessing it was due to the thread being locked by the second socket or something.

i get a tftp timeout from vxworks

I wrote some code on VxWorks to download a file from a TFTP server using tftpLib but the get gives me a timeout:
ERR [TFTP] tftpSend:479: Transfer Timed Out.
ERR [TFTP] tftpGet:1077: File transfer error.
Error has occurred: 4915207
which isn't right as the host is reachable:
ping("3.94.213.53",3)
Pinging 3.94.213.53 (3.94.213.53) with 64 bytes of data:
Reply from 3.94.213.53 bytes=64 ttl=63 seq=0 time<1ms
Reply from 3.94.213.53 bytes=64 ttl=63 seq=1 time<1ms
Reply from 3.94.213.53 bytes=64 ttl=63 seq=2 time<1ms
and when I do this from the Linux shell, it works just as expected:
tftp -r "artifacts/ngfm.bin" -g 3.94.213.53
What might be the problem here?
The get section of my code looks like:
pFile = fopen("flash:/ngfm.bin","wb");
if (pFile != NULL) {
/* Get file from TFTP server and write it to the file descriptor */
if (OK == (status = tftpGet (pTftpDesc, pFilename, pFile, TFTP_CLIENT))) {
printf("tftpGet() successful\n");
} else {
printf("Error has occurred: %d\n", errno); // errno is where the error is stored
}
} else {
printf("Bad file pointer pFile");
}
edit:
The code I have above the get portion is:
/*Initiate TFTP session*/
if ((pTftpDesc = tftpInit ()) == NULL)
printf("Error on tftpInit()\n");
/*connect to TFTP host and set transfer mode*/
if ((tftpPeerSet (pTftpDesc, pHost, port) == ERROR) ||
(tftpModeSet (pTftpDesc, pMode) == ERROR)) {
(void) tftpQuit (pTftpDesc);
printf("Error on tftpPeerSet()\n");
return ERROR;
}
I believe your problem is caused by lack of calling of tftpModeSet - http://www.vxdev.com/docs/vx55man/vxworks/ref/tftpLib.html#tftpModeSet
So add:
tftpModeSet(pTftpDesc, "binary");
This will prevent your binary file from causing the stream to die off on the first \n
Okay, turns out that TFTP is a no go in my situation.
I hooked up Wireshark and saw that my client is getting through to the server just fine on port 69. I previously have also made sure that I have port forwarding on port 69 setup in my iptable rules properly. Now I just read this on Wikipedia:
Data transfer is initiated on port 69, but the data transfer ports are
chosen independently by the sender and receiver during initialization
of the connection. The ports are chosen at random according to the
parameters of the networking stack, typically from the range of
ephemeral ports
i.e. TFTP won't work for me because I need NAT and it has to be secure. I'll need to go with a protocol that's connection orriented, ftp e.g.
I found that the standar VxWorks library also contains ftpLib.h (http://www.vxdev.com/docs/vx55man/vxworks/ref/ftpLib.html#ftpLs) that will hopefully resolve my NAT issues as FTP works with connection based TCP.

Sending push notifications with NSOutputStream returns -9844

I'm trying to write OS X app sending push notifications with use of NSStreams. Everything seems to be fine but i get back error -9844 when sending data to gateway.sandbox.push.apple.com. From SecureTransport.h
errSSLConnectionRefused = -9844, /* peer dropped connection before responding */
This is how I create and open output stream:
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)#"gateway.sandbox.push.apple.com", 2195, &readStream, &writeStream);
self.outputPushStream = (__bridge_transfer NSOutputStream *)writeStream;
[self.outputPushStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
self.outputPushStream.delegate = self;
[self.outputPushStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.outputPushStream open];
And upon receiving NSStreamEventOpenCompleted event in NSStream delegate I send data:
const uint8_t *data = pushData.bytes;
[self.outputPushStream write:data maxLength:pushData.length];
I'm guessing that this may be some issue with certificates. I've got dev certificate and key for push notifications in keychain. I don't have any experience with SSL or NSStream so I don't really know where to look next.
I'm not sure if certificate in a keychain is enough for CFStreamCreatePairWithSocketToHost to set up an ssl connection. I found this code which does pretty much the same you want to do, but it passes certificate with code (see configureStreams and certificateArray methods).

Resources