From ee61dc9a598fa6fcb55e4029f1d2e81d973befea Mon Sep 17 00:00:00 2001 From: Diego Waxemberg Date: Fri, 2 Jan 2015 19:15:47 -0500 Subject: [PATCH] implemented wake on lan --- Limelight.xcodeproj/project.pbxproj | 10 +++ Limelight/Network/WakeOnLanManager.h | 16 ++++ Limelight/Network/WakeOnLanManager.m | 82 +++++++++++++++++++ .../ViewControllers/MainFrameViewController.m | 31 +++++-- 4 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 Limelight/Network/WakeOnLanManager.h create mode 100644 Limelight/Network/WakeOnLanManager.m diff --git a/Limelight.xcodeproj/project.pbxproj b/Limelight.xcodeproj/project.pbxproj index f7647f5..809ca62 100644 --- a/Limelight.xcodeproj/project.pbxproj +++ b/Limelight.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ FB290E7B19B38036004C83CF /* iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FB290E7A19B38036004C83CF /* iPhone.storyboard */; }; FB4678ED1A50C40900377732 /* OnScreenControls.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678EC1A50C40900377732 /* OnScreenControls.m */; }; FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678F91A55FFAD00377732 /* DiscoveryManager.m */; }; + FB4678FF1A565DAC00377732 /* WakeOnLanManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */; }; + FB4679011A57048000377732 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB4679001A57048000377732 /* CoreFoundation.framework */; }; FB7E794419C8B71B00A15F68 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB7E794319C8B71B00A15F68 /* libiconv.dylib */; }; FB89462819F646E200339C8A /* CryptoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89460619F646E200339C8A /* CryptoManager.m */; }; FB89462919F646E200339C8A /* mkcert.c in Sources */ = {isa = PBXBuildFile; fileRef = FB89460719F646E200339C8A /* mkcert.c */; }; @@ -112,6 +114,9 @@ FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Limelight 0.3.0.xcdatamodel"; sourceTree = ""; }; FB4678F81A55FFAD00377732 /* DiscoveryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiscoveryManager.h; sourceTree = ""; }; FB4678F91A55FFAD00377732 /* DiscoveryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiscoveryManager.m; sourceTree = ""; }; + FB4678FD1A565DAC00377732 /* WakeOnLanManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WakeOnLanManager.h; sourceTree = ""; }; + FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WakeOnLanManager.m; sourceTree = ""; }; + FB4679001A57048000377732 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; FB7E794319C8B71B00A15F68 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; FB89460519F646E200339C8A /* CryptoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoManager.h; sourceTree = ""; }; FB89460619F646E200339C8A /* CryptoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoManager.m; sourceTree = ""; }; @@ -262,6 +267,7 @@ FB290CF819B2C406004C83CF /* CoreData.framework in Frameworks */, FB290CF619B2C406004C83CF /* UIKit.framework in Frameworks */, FB290CF219B2C406004C83CF /* Foundation.framework in Frameworks */, + FB4679011A57048000377732 /* CoreFoundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -309,6 +315,7 @@ FB290CF019B2C406004C83CF /* Frameworks */ = { isa = PBXGroup; children = ( + FB4679001A57048000377732 /* CoreFoundation.framework */, FBD349571A003F05002D2A60 /* libsqlite3.dylib */, FB89468F19F6AFB800339C8A /* libs */, FB7E794319C8B71B00A15F68 /* libiconv.dylib */, @@ -415,6 +422,8 @@ FBD3494219FC9C04002D2A60 /* AppManager.m */, FB4678F81A55FFAD00377732 /* DiscoveryManager.h */, FB4678F91A55FFAD00377732 /* DiscoveryManager.m */, + FB4678FD1A565DAC00377732 /* WakeOnLanManager.h */, + FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */, ); path = Network; sourceTree = ""; @@ -759,6 +768,7 @@ FB89463419F646E200339C8A /* Utils.m in Sources */, FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */, FB89462F19F646E200339C8A /* Connection.m in Sources */, + FB4678FF1A565DAC00377732 /* WakeOnLanManager.m in Sources */, FB89462919F646E200339C8A /* mkcert.c in Sources */, FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */, FBDE86E919F82315001C18A8 /* App.m in Sources */, diff --git a/Limelight/Network/WakeOnLanManager.h b/Limelight/Network/WakeOnLanManager.h new file mode 100644 index 0000000..b820dec --- /dev/null +++ b/Limelight/Network/WakeOnLanManager.h @@ -0,0 +1,16 @@ +// +// WakeOnLanManager.h +// Limelight +// +// Created by Diego Waxemberg on 1/2/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "Host.h" + +@interface WakeOnLanManager : NSObject + ++ (void) wakeHost:(Host*)host; + +@end diff --git a/Limelight/Network/WakeOnLanManager.m b/Limelight/Network/WakeOnLanManager.m new file mode 100644 index 0000000..8528b71 --- /dev/null +++ b/Limelight/Network/WakeOnLanManager.m @@ -0,0 +1,82 @@ +// +// WakeOnLanManager.m +// Limelight +// +// Created by Diego Waxemberg on 1/2/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "WakeOnLanManager.h" +#import "Utils.h" +#import +#import +#import +#import + +@implementation WakeOnLanManager + +static const int numPorts = 5; +static const int ports[numPorts] = {7, 9, 47998, 47999, 48000}; + ++ (void) wakeHost:(Host*)host { + NSData* wolPayload = [WakeOnLanManager createPayload:host]; + CFDataRef dataPayload = CFDataCreate(kCFAllocatorDefault, [wolPayload bytes], [wolPayload length]); + CFSocketRef wolSocket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, NULL, NULL); + if (!wolSocket) { + NSLog(@"Failed to create WOL socket"); + return; + } + NSLog(@"WOL socket created"); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_len = sizeof(addr); + + + for (int i = 0; i < 2; i++) { + // try all ip addresses + if (i == 0) { + inet_aton([host.localAddress UTF8String], &addr.sin_addr); + } else { + inet_aton([host.externalAddress UTF8String], &addr.sin_addr); + } + + CFDataRef dataAddress = CFDataCreate(kCFAllocatorDefault, (unsigned char*)&addr, sizeof(addr)); + + // try all ports + for (int j = 0; j < numPorts; j++) { + addr.sin_port = htons(ports[j]); + NSLog(@"Sending WOL packet"); + CFSocketSendData(wolSocket, dataAddress, dataPayload, 0); + } + CFRelease(dataAddress); + } + CFRelease(dataPayload); +} + ++ (NSData*) createPayload:(Host*)host { + NSMutableData* payload = [[NSMutableData alloc] initWithCapacity:102]; + + // 6 bytes of FF + UInt8 header = 0xFF; + for (int i = 0; i < 6; i++) { + [payload appendBytes:&header length:1]; + } + + // 16 repitiions of MAC address + NSData* macAddress = [self macStringToBytes:host.mac]; + for (int j = 0; j < 16; j++) { + [payload appendData:macAddress]; + } + + return payload; +} + ++ (NSData*) macStringToBytes:(NSString*)mac { + NSString* macString = [mac stringByReplacingOccurrencesOfString:@":" withString:@""]; + NSLog(@"MAC: %@", macString); + return [Utils hexToBytes:macString]; +} + +@end diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 7343e0c..76d9e31 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -18,6 +18,7 @@ #import "SettingsViewController.h" #import "DataManager.h" #import "Settings.h" +#import "WakeOnLanManager.h" @implementation MainFrameViewController { NSOperationQueue* _opQueue; @@ -115,12 +116,30 @@ static StreamConfiguration* streamConfig; - (void)hostLongClicked:(Host *)host { NSLog(@"Long clicked host: %@", host.name); UIAlertController* longClickAlert = [UIAlertController alertControllerWithTitle:host.name message:@"" preferredStyle:UIAlertControllerStyleActionSheet]; - [longClickAlert addAction:[UIAlertAction actionWithTitle:@"Unpair" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - [hMan executeRequestSynchronously:[hMan newUnpairRequest]]; - }); - }]]; + if (host.online) { + [longClickAlert addAction:[UIAlertAction actionWithTitle:@"Unpair" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + [hMan executeRequestSynchronously:[hMan newUnpairRequest]]; + }); + }]]; + } else { + [longClickAlert addAction:[UIAlertAction actionWithTitle:@"Wake" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ + UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:@"Wake On Lan" message:@"" preferredStyle:UIAlertControllerStyleAlert]; + [wolAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; + if (host.pairState != PairStatePaired) { + wolAlert.message = @"Cannot wake host because you are not paired"; + } else if (host.mac == nil || [host.mac isEqualToString:@"00:00:00:00:00:00"]) { + wolAlert.message = @"Host MAC unknown, unable to send WOL Packet"; + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [WakeOnLanManager wakeHost:host]; + }); + wolAlert.message = @"Sent WOL Packet"; + } + [self presentViewController:wolAlert animated:YES completion:nil]; + }]]; + } [longClickAlert addAction:[UIAlertAction actionWithTitle:@"Remove Host" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) { @synchronized(hostList) { [hostList removeObject:host];