Apple published with iOS 8 a new Framework "NetworkExtension".
I want to start a VPN Connection out of an app with the NEVPNManager, or has this Framework another use?
Has somebody information or an example about this Framework?
I can´t find information about it on the developer.apple.com website, only in the header files.
Thanks
The code would look something like this (exact implementation depends on the type of VPN):
NEVPNManager *manager = [NEVPNManager sharedManager];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(vpnConnectionStatusChanged) name:NEVPNStatusDidChangeNotification object:nil];
NEVPNProtocolIPSec *protocol = [[NEVPNProtocolIPSec alloc] init];
protocol.username = #“[Your username]”;
protocol.passwordReference = [KeyChainAccess loadDataForServiceNamed:#“[Your Service Name]"];
protocol.serverAddress = #“[Your Server Address]“;
protocol.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
protocol.localIdentifier = #“[Your Local identifier]”;
protocol.remoteIdentifier = #“[Your Remote identifier]”;
protocol.useExtendedAuthentication = NO;
protocol.identityData = [Your VPN certification private key];
protocol.disconnectOnSleep = NO;
[manager setProtocol:protocol];
[manager setOnDemandEnabled:NO];
[manager setLocalizedDescription:#"VPN"];
NSArray *array = [NSArray new];
[manager setOnDemandRules: array];
NSLog(#"Connection desciption: %#", manager.localizedDescription);
NSLog(#"VPN status: %i", manager.connection.status);
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
// do config stuff
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
}];
}];
NSError *startError;
[[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
if(startError) {
NSLog(#"Start error: %#", startError.localizedDescription);
}
Related
I´m trying to implement local notifications in a project made in QT, this is made through KNotifications, the current issue is with MACOS, the following code is the one being used:
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions: (UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable per_error) {
NSLog(#"PERMISSION: %d - %#", granted, per_error);
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = #"TEST TITLE";
content.body = #"TEST BODY";
content.sound = UNNotificationSound.defaultSound;
NSUUID *uuid = [NSUUID UUID];
NSString *str = [uuid UUIDString];
UNNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats: NO];
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier: str content:content trigger:trigger];
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable req_error) {
NSLog(#"REQUEST LOG: %#", req_error);
}];
}];
When I run the code in an empty native XCode application, everything works fine, but when I run it on the QT / KNotifications project I get the following
Error Domain=UNErrorDomain Code=1 "Notifications are not allowed for this application"
I have checked and my test application appears right away on "Systems Preferences" but the other is not listed
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);
}
}];
}
}];
I am trying to connect vpn using NETWORK EXTENSION framework, but have some issue while get persistent reference of password stored in keychain. It [NSLog(#"password: %#", [manager protocol].passwordReference);] returns null after a call to [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {...} but when I try to log before calling saveToPreferencesWithCompletionHandler it give me a valid reference in keychain. When I use this code block it gets all the details I have stored in keychain for persistent reference.
NSDictionary *query = #{ (__bridge id)kSecValuePersistentRef: (__bridge NSData *)(KeychainUserPass.passwordPersistentReference),
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue };
CFDictionaryRef passwordDict = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&passwordDict);
NSLog(#"status: %ld, passwordDict: %#", status, (__bridge NSDictionary*)passwordDict);
NSLog(#"keychain password: %#", p.passwordReference);
But why it does not get any detail when I use NEVPNManager class object to store password reference? and it prompts me to enter password whenever I tried to connect vpn instead of getting it from keychain?
Here is the code:
NEVPNProtocolIKEv2 *p = [[NEVPNProtocolIKEv2 alloc] init];
p.username = #"username";
p.passwordReference = (__bridge NSData *)(KeychainUserPass.passwordPersistentReference);
p.serverAddress = #"hostname";
p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
p.serverCertificateIssuerCommonName = #"Certificate Issuer name";
p.serverCertificateCommonName = #"Certificate name";
p.identityData = //p12;
p.identityDataPassword = #"password for p12";
p.localIdentifier = #"local identifier";
p.remoteIdentifier = #"remote identifier";
p.useExtendedAuthentication = YES;
p.disconnectOnSleep = NO;
[manager setProtocol:p];
[manager setOnDemandEnabled:YES];
[manager setLocalizedDescription:#"VPN Profile"];
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if(error) {
NSLog(#"Save error: %#", error);
}
else {
NSLog(#"Saved!");
NSLog(#"OnDemandEnable: %d", [manager isOnDemandEnabled]);
NSLog(#"onDemandRules: %#", [manager onDemandRules]);
NSLog(#"username: %#", [manager protocol].username);
NSLog(#"password: %#", [manager protocol].passwordReference);
}
}];
}
P.S: Everything works fine except this issue.
You have to load the preferences before you can alter them. Make sure to run loadFromPreferencesWithCompletionHandler and then create your protocol and make your updates.
I am adding share functionalities on my facebook app.Like When a "saying" is selected there is a button for sharing that "saying" on facebook.And while clicking this button I can only see the shared saying on my facebook page,there isnt any information about my ios app.How can I make everyone knows that this saying is shared through my iOS app? Please help me....
I may be a little late. Hope this helps.
You have to use the Accounts framework and the Social framework to share with your app name.
First make sure you have set up your App on Facebook correctly. Then you can use the Facebook App ID to share your posts through your app.
Here is a sample code that shows you how to use the Accounts framework with the Social Framework :
ACAccountType * facebookAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// At first, we only ask for the basic read permission
NSArray * permissions = #[#"email"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"275485699289493", ACFacebookAppIdKey, permissions, ACFacebookPermissionsKey, ACFacebookAudienceOnlyMe, ACFacebookAudienceKey, nil];
NSArray *accounts = [self.accountStore accountsWithAccountType:facebookAccountType];
//it will always be the last object with single sign on
self.facebookAccount = [accounts lastObject];
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:dict completion:^(BOOL granted, NSError *error) {
if (granted && error == nil) {
/**
* The user granted us the basic read permission.
* Now we can ask for more permissions
**/
NSArray *readPermissions = #[ #"publish_actions"];
[dict setObject:readPermissions forKey: ACFacebookPermissionsKey];
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:dict completion:^(BOOL granted, NSError *error) {
if(granted && error == nil) {
NSDictionary *parameters = #{#"message": #"This Should Work Perfectly !! "};
NSURL *feedURL = [NSURL URLWithString:#"https://graph.facebook.com/me/feed"];
SLRequest *feedRequest = [SLRequest
requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodPOST
URL:feedURL
parameters:parameters];
feedRequest.account = self.facebookAccount;
[feedRequest performRequestWithHandler:^(NSData *responseData,
NSHTTPURLResponse *urlResponse, NSError *error)
{
// Handle response
}];
} else {
NSLog(#"error is: %#",[error description]);
}
}];
} else {
NSLog(#"error is: %#",[error description]);
}
}];
}
I'm developing an app that allows users to upload their photos to my web server. I've recently added support for uploading multiple files at once: users can select photos from their iPhone album and upload then to the server.
Uploading one file is no problem, however, when I try to upload multiple files, I get the following error:
The operation couldn't be completed (kCFErrorDomainCFNetwork error 303.)
The code I'm using for uploading the files is the following:
// start the uploading for each photo
for(int i = 0; i < photosArray.count; i++)
{
Photo *currentPhoto = [photosArray objectAtIndex:i];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"upload", #"command", UIImageJPEGRepresentation(currentPhoto.image,70),#"file", [NSNumber numberWithInt:album.albumId], #"albumId", currentPhoto.title, #"title", currentPhoto.description, #"description", nil];
[[AFAPI sharedInstance] commandWithParams:params onCompletion:^(NSDictionary *json)
{
//completion
if (![json objectForKey:#"error"])
{
// hide the UIProgressView and show the detail label
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
UIProgressView *pv = (UIProgressView *) [cell viewWithTag:4];
pv.hidden = YES;
UILabel *detailLabel = (UILabel *) [cell viewWithTag:3];
detailLabel.text = #"Upload completed";
detailLabel.hidden = NO;
}
else
{
//error :(
NSString* errorMsg = [json objectForKey:#"error"];
[UIAlertView error:errorMsg];
}
}
onUploadProgress:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite)
{
// ...
}];
}
I'm enumerating the photosArray with a for loop and for every photo it finds, it uploads the image (currentPhoto.image). The implementation of the commandWithParams function is:
-(void)commandWithParams:(NSMutableDictionary*)params onCompletion:(JSONResponseBlock)completionBlock onUploadProgress:(void (^)(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))uploadProgressBlock
{
NSData* uploadFile = nil;
if ([params objectForKey:#"file"])
{
uploadFile = (NSData*)[params objectForKey:#"file"];
[params removeObjectForKey:#"file"];
}
NSMutableURLRequest *apiRequest =
[self multipartFormRequestWithMethod:#"POST"
path:kAPIPath
parameters:params
constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
if (uploadFile) {
[formData appendPartWithFileData:uploadFile
name:#"file"
fileName:#"photo.jpg"
mimeType:#"image/jpeg"];
}
}];
AFJSONRequestOperation* operation = [[AFJSONRequestOperation alloc] initWithRequest: apiRequest];
[operation setUploadProgressBlock:uploadProgressBlock];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//success!
completionBlock(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure :(
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:#"error"]);
}];
[operation start];
}
Sorry for the long code part but I really can't figure out how to solve this error. I've tried to use a NSOperationQueue but that also gave the same error.
I've searched on internet, and I figured out that error 303 in the CFNetwork means that the HTTP response couldn't be parsed. Could it be that the problem is in the web service? If so, I can also give the php part where I handle the uploaded file :)
Thanks in advance!
I think that this was a bug in AFNetworking. I had the exact same issue, but upgrading to the latest version downloaded on August 24, 2012 resolved the problem for me.