Send Data to a multicast socket using GCDAsyncUDPSocket in local network - objective-c

Im working in an app where you suscribe to a multicast socket to listen to other devices that post to that socket. When listening everything is working fine. The problem is when I try to post to that socket in a local network. It says "Network unreachable" and I can't send the data. My code is like this.
PORT 5775
SOCKETADDRESS #"225.4.5.6"
Listener
self.socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
if (![self.socket bindToPort:PORT error:&error])
{
NSLog(#"Error binding to port: %#", error);
return;
}
if(![self.socket joinMulticastGroup:SOCKETADDRESS error:&error]){
// NSLog(#"Error connecting to multicast group: %#", error);
// return;
}
if (![self.socket beginReceiving:&error])
{
NSLog(#"Error receiving: %#", error);
return;
}
Client(Sender)
self.udpSender = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
if (![self.udpSender bindToPort:BRD_PORT error:&error])
{
NSLog(#"Error binding to port: %#", error);
return;
}
if(![self.udpSender enableBroadcast:YES error:&error]){
// NSLog(#"broadcast s%#", error);
//return;
}
And I try to write like this.
[self.udpSender sendData:input toHost:SOCKETADDRESS port:PORT withTimeout:-1 tag:0];
This codes work when I have internet access. But when I connect to a local network the sender says "Network is unreachable". I don't know what I'm doing wrong. Hope you can help me. Thanks.

Related

OAuthTokenRequest using STTwitterAPI fails

Using following code to get OAuth token but it always fails with error
Your credentials do not allow access to this resource
I have taken following code sample from
https://github.com/nst/STTwitter/blob/master/demo_cli/reverse_auth/main.m
Even I have allowed Read and Write access in application settings at
https://apps.twitter.com/app/XXXX/permissions
STTwitterAPI *twitter = [STTwitterAPI twitterAPIWithOAuthConsumerName:nil consumerKey:#"MY CONSUMER KEY" consumerSecret:#"MY CONSUMER SECRET"];
[twitter postReverseOAuthTokenRequest:^(NSString *authenticationHeader) {
STTwitterAPI *twitterAPIOS = [STTwitterAPI twitterAPIOSWithFirstAccount];
[twitterAPIOS verifyCredentialsWithSuccessBlock:^(NSString *username) {
[twitterAPIOS postReverseAuthAccessTokenWithAuthenticationHeader:authenticationHeader successBlock:^(NSString *oAuthToken, NSString *oAuthTokenSecret, NSString *userID, NSString *screenName) {
NSLog(#"REVERSE AUTH OK");
} errorBlock:^(NSError *error) {
NSLog(#"ERROR, %#", [error localizedDescription]);
}];
} errorBlock:^(NSError *error) {
NSLog(#"ERROR");
}];
} errorBlock:^(NSError *error) {
NSLog(#"ERROR");
}];
Any help, where I am going wrong.

"Couldn't send a valid signature" error when using CloudKit

Anyone know what could be causing this error which is returned in the query block below:
<CKError 0x7f8d5ba27a10: "Internal Error" (1/4000); "Couldn't send a valid signature">
Here is my code snippet:
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [container publicCloudDatabase];
//CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:container] publicCloudDatabase];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"doctorName = %#", #"Dr. Harry White"];
CKQuery *query = [[CKQuery alloc] initWithRecordType:#"WellnessTest" predicate:predicate];
[publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error)
{
if (error)
{
// Error handling for failed fetch from public database
NSLog(#"ERROR: %#", error.description);
}
else
{
// Display the fetched records
NSLog(#"Results: %#", results.description);
}
}];
On the iPhone/iPad simulator go to Settings > iCloud and then log in using your Apple ID.
If you're testing your app against CloudKit's Production environment, you may still have this error even if you have signed into iCloud on the simulator. In this case, running your app on a device would fix the error.

Can't create IPSEC connection using NEVPNManager on iOS

I'm trying to create an IPSEC VPN connection in my iOS app.
My code for setting up the configuration looks like this:
-(void)setUpConfig
NEVPNManager *manager = [NEVPNManager sharedManager];
int status = manager.connection.status;
if (status == NEVPNStatusConnected) {
manager.connection stopVPNTunnel];
} else {
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
NSError *startError;
if (error) {
NSLog(#"Load config failed [%#]", error.localizedDescription);
return;
}
NEVPNProtocolIPSec *p = (NEVPNProtocolIPSec *)self.manager.protocol;
if (!p) {
p = [[NEVPNProtocolIPSec alloc] init];
}
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"base64_encoded_cert" ofType:#"txt"];
NSString *certBase64String = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
NSString *certPassword = #"cert_import_password";
NSString *vpnUsername = #"myUsername";
NSString *vpnPassword = #"myPassword";
NSString *url = #"my.server.address";
// This saves my credentials to the keychain and returns a persistent keychain reference
NSData *passRef = [self addVPNCredentialsToKeychain:vpnUsername withPassword:vpnPassword];
p.username = vpnUsername;
p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
p.serverAddress = url;
p.passwordReference = passRef;
p.identityData = [NSData dataWithBase64EncodedString:certBase64String];
p.identityDataPassword = certPassword;
p.disconnectOnSleep = NO;
p.useExtendedAuthentication = YES;
[manager setProtocol:p];
[manager setOnDemandEnabled:NO];
[manager setLocalizedDescription:#"My VPN"];
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if(error) {
NSLog(#"Save error: %#", error);
} else {
NSLog(#"Saved!");
}
}];
}];
}
}
-(NSData*)addVPNCredentialsToKeychain:(NSString*)username withPassword:(NSString*)password
{
NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];
NSData *encodedIdentifier = [username dataUsingEncoding:NSUTF8StringEncoding];
keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
keychainItem[(__bridge id)kSecAttrDescription] = #"A password used to authenticate on a VPN server";
keychainItem[(__bridge id)kSecAttrGeneric] = encodedIdentifier;
keychainItem[(__bridge id)kSecAttrAccount] = encodedIdentifier;
keychainItem[(__bridge id)kSecAttrService] = [[NSBundle mainBundle] bundleIdentifier];
keychainItem[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
keychainItem[(__bridge id)kSecReturnPersistentRef] = #YES;
CFTypeRef typeResult = nil;
OSStatus sts = SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, &typeResult);
NSLog(#"Error Code: %d", (int)sts);
if(sts == noErr) {
NSData *theReference = (__bridge NSData *)typeResult;
return theReference;
} else {
keychainItem[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding]; //Our password
OSStatus sts = SecItemAdd((__bridge CFDictionaryRef)keychainItem, &typeResult);
NSLog(#"Error Code: %d", (int)sts);
NSData *theReference = (__bridge NSData *)(typeResult);
return theReference;
}
return nil;
}
I then attempt to open a VPN connection like so:
-(void)connect
NEVPNManager *manager = [NEVPNManager sharedManager];
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
NSError *startError;
[manager.connection startVPNTunnelAndReturnError:&startError];
if(startError) {
NSLog(#"Start error: %#", startError.localizedDescription);
}
}];
}
And this does work under most conditions. The problem I am experiencing is that after factory restoring a device (on iOS 8, of course), and attempting to go through this setup and connection, my profile installs just fine, but the VPN fails to connect. In fact, my interpretation is that it is failing to attempt to connect.
After factory restoring a device and attempting to connect using my method, the following shows up in the device logs:
<Notice>: NESMLegacySession[MyVPN:BB73C098-B22E-46D3-9491-2A6D9F559F8F]: Received a start command from VPNApp[256], but start was rejected
Going into the Settings app and attempting to manually toggle the VPN using the switch under "Bluetooth" results in the switch turning on for a split second and then immediately going by to off. In this case, the following log is produced:
<Warning>: -[VPNBundleController _vpnNetworkingIsDisabled]: Airplane mode: 0, WiFi Enabled: 1
In both cases, no error dialog is produced when the VPN fails to start connecting - just the logs.
I can get around this problem by navigating to Settings > General > VPN. Once having just gone to that page (i.e. Not toggling VPN there), I can then control VPN just fine. Even going to that page before a VPN configuration is even installed results in me being able to connect just fine after installing a configuration.
My goal is to be able to start the VPN connection without having to first go to that VPN page in Settings. Can anyone shed some light on the situation? It seems to me like I'm missing something to first enable VPN connections.
This appears because VPN configuration stayed disabled by default for initial VPN connection.
You must enable VPN before saveToPreferencesWithCompletionHandler.
[[NEVPNManager sharedManager] setEnabled:YES];
Example:
[[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler: ^(NSError *error) {
if (error) {
NSLog(#"Load error: %#", error);
}
else {
// No errors! The rest of your codes goes here...
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.serverAddress = #"VPN SERVER ADDRESS";
p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
p.localIdentifier = #"Local identifier";
p.remoteIdentifier = #"Remote identifier";
p.useExtendedAuthentication = YES;
p.identityData = [NSData dataWithBase64EncodedString:certBase64String];;
p.identityDataPassword = #"identity password";
p.disconnectOnSleep = NO;
// Set protocol
[[NEVPNManager sharedManager] setProtocol:p];
// Set on demand
NSMutableArray *rules = [[NSMutableArray alloc] init];
NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];
[rules addObject:connectRule];
[[NEVPNManager sharedManager] setOnDemandRules:rules];
// Set localized description
[[NEVPNManager sharedManager] setLocalizedDescription:#"Description"];
// Enable VPN
[[NEVPNManager sharedManager] setEnabled:YES];
// Save to preference
[[NEVPNManager sharedManager] saveToPreferencesWithCompletionHandler: ^(NSError *error) {
NSLog(#"Save VPN to preference complete");
if (error) {
NSLog(#"Save to preference error: %#", error);
}
}];
}
}];

How to use RACMulticastConnection for app components notification

Short version:
Is it possible to use RACMulticastConnection in the same way as NSNotificationCenter? I mean to keep the subscribed blocks valid even for another call [connection connect]?
Long version:
Among different subscribers I share a reference to RACMulticastConnection. Those who initiate the request pass loginRequest!=nil and those subscribers who want just listen use loginRequest==nil:
RACMulticastConnection *connection = [self.appModel loginRequest:loginRequest];
[connection connect]; //I initiate the request
[connection.signal subscribeNext:^(RACTuple* responseTuple) {
} error:^(NSError *error) {
} completed:^{
}];
In other modul I just subscribe and listen:
RACMulticastConnection *connection = [self.appModel loginRequest:nil];
[connection.signal subscribeNext:^(RACTuple* responseTuple) {
} error:^(NSError *error) {
} completed:^{
}];
When I call the [connection connect]; everything works fine. The subscribers are notified. But if I want to repeat the request to the server again with [connection connect]; I just receive successful signal with the old responses.
Basic idea is I want to create RACMulticastConnection once and share it for potential subscribers. Those who listen pass nil arguments, those who initiate the request pass not nil argument and call [connection connect]; But it does not trigger the block defined in RACSignal createSignal:.
The RACSignal is created just once when RACMulticastConnection does not exist. self.loginRequestConnection is property of model. The property is shared in the application to subscribers:
- (RACMulticastConnection*) loginRequest:(LoginRequest*)request {
self.loginRequest = request;
if(! self.loginRequestConnection) { // the instance of RACMulticastConnection shared among the subscribers
#weakify(self);
RACSignal* networkRequest = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
__block Response* blockResponse = nil;
#strongify(self);
[_serviceClient login:self.loginRequest success:^(TSNApiResponse *response, TSNError *error) {
blockResponse = response;
[subscriber sendNext:RACTuplePack(response, error)];
[subscriber sendCompleted];
} failure:^(TSNError *error) {
[self cleanUpRequestOnError:error subscriber:subscriber blockResponse:blockResponse blockRequest:self.loginRequest];
[subscriber sendError:error];
}];
return [RACDisposable disposableWithBlock:^{
[_serviceClient cancelRequest:self.loginRequest];
}];
}];
self.loginRequestConnection = [networkRequest multicast:[RACReplaySubject subject]];
}
return self.loginRequestConnection;
}
Is there any correct way how to make the connection trigger again the block in the RACSignal? Thank you.
I have used FBKVOController. The code done with FBKVOController is fraction of the implementation done with ReactiveCocoa:
- (void) loginRequest:(TSNLoginRequest*)request {
[_serviceClient login:request success:^(TSNApiResponse *response, TSNError *error) {
self.loginResponseTuple = [TSNResponseTuple responseTuple:response error:error];
} failure:^(TSNError *error) {
self.loginResponseTuple = [TSNResponseTuple responseTuple:nil error:error];
}];
}
Issuing the request:
[self.appModel loginRequest:loginRequest];
Observing the response:
[_KVOController observe:self.appModel keyPath:#"loginResponseTuple" options:NSKeyValueObservingOptionNew block:^(TSNStartupScreenViewModel* observer, TSNAppModel* observed, NSDictionary *change) {
TSNResponseTuple* responseTuple = change[NSKeyValueChangeNewKey];
#strongify(self);
if([responseTuple.error isError]) {
[TSNAppUtilities showError:(TSNError *)(responseTuple.error) completion:^(OHAlertView *alert, NSInteger buttonIndex) {
if(buttonIndex == alert.firstOtherButtonIndex) {
// ... process selection
}
}];
}
}];

GCDAsyncUdpSocket Multicast Gets HTTP 412 Response

I am building a iOS application that sends out a multicast message (SSDP) and listens for responses from devices that implement that protocol. I am using GCDAsyncUdpSocket to create the UDP socket to broadcast the SSDP message and receive responses. The problem that I am having is that the message the application receives from the all of the devices on the network is the same: a HTTP 412 response.
Here is my code:
Initialize socket
#interface ViewController ()
{
GCDAsyncUdpSocket *udpSocket;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![udpSocket bindToPort:0 error:&error])
{
NSLog(#"Error binding: %#", [error description]);
return;
}
if (![udpSocket beginReceiving:&error])
{
NSLog(#"Error receiving: %#", [error description]);
return;
}
[udpSocket enableBroadcast:YES error:&error];
if (error != nil)
{
NSLog(#"Error enableing broadcast: %#", [error description]);
return;
}
NSLog(#"Socket Created");
}
Create Socket
- (IBAction)socketFind:(id)sender
{
NSString *str = [[NSString alloc] initWithString:#"M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp discover\"\r\nMX: 3\r\nST: ssdp:all\r\nUSER-AGENT: iOS UPnP/1.1 TestApp/1.0\r\n\r\n"];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[udpSocket sendData:data toHost:#"239.255.255.250" port:1900 withTimeout:-1 tag:0];
NSLog(#"Sent Data");
}
Data Received Callback
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
NSLog(#"Did Receive Data");
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
NSLog(#"Message: %#",msg);
}
else
{
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
NSLog(#"Unknown Message: %#:%hu", host, port);
}
}
When the didReceiveData function is called, I get the following message:
Message: HTTP/1.1 412 Precondition Failed
Connection:close
CONTENT-LENGTH:0
This issue was with the M-Search string. I was missing the colon between sspd and discover. If you are getting a 412 response, I would make sure that you are formatting everything correctly. Turns out, that was my issue...