CVE-2015-9231: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: iTerm2 3.x before 3.1.1 allows remote attackers to discover passwords by reading DNS queries. A new (default) feature was added to iTerm2 version 3.0.0 (and unreleased 2.9.x versions such as 2.9.20150717) that resulted in a potential information disclosure. In an attempt to see whether the text under the cursor (or selected text) was a URL, the text would be sent as an unencrypted DNS query. This has the potential to result in passwords and other sensitive information being sent in cleartext without the user being aware.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
CVSS v3 base score: 7.5
Published: 2017-09-20T20:29Z
Repository: https://github.com/gnachman/iTerm2
Language: Objective-C
Files changed: iTerm2.xcodeproj/project.pbxproj, sources/PTYTextView.m, sources/iTermAdvancedSettingsModel.h, sources/iTermAdvancedSettingsModel.m, sources/iTermTextDrawingHelper.h, sources/iTermTextDrawingHelper.m
Commit date: 2017-09-20 10:16:28 -0700
Commit message: Fist swipe at removing DNS code

Diff: diff --git a/iTerm2.xcodeproj/project.pbxproj b/iTerm2.xcodeproj/project.pbxproj
index 63a5a290..c7328a89 100644
--- a/iTerm2.xcodeproj/project.pbxproj
+++ b/iTerm2.xcodeproj/project.pbxproj
@@ -628,11 +628,10 @@
 		1D6A5FDF140D7AA000DE19F8 /* IBarCursorXMR.png in Resources */ = {isa = PBXBuildFile; fileRef = 1D6A5FDE140D7AA000DE19F8 /* IBarCursorXMR.png */; };
 		1D6C18BE12951A3C00937A4A /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DEB293D1288899A00B2CB9F /* Carbon.framework */; };
 		1D6C4D5A122329F000E0AA3E /* ColorPresets.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1D6C4D59122329F000E0AA3E /* ColorPresets.plist */; };
 		1D6C50A71226EEFB00E0AA3E /* ProfileListView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D6C50A51226EEFB00E0AA3E /* ProfileListView.h */; };
 		1D6ED85619AEA20D005A7799 /* URLAction.h in Headers */ = {isa = PBXBuildFile; fileRef = A6CFDAD0185D2587005DC94B /* URLAction.h */; };
-		1D6ED85719AEA20D005A7799 /* AsyncHostLookupController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */; };
 		1D6ED85819AEA20D005A7799 /* LineBufferPosition.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D78B55C183EE1C000014D49 /* LineBufferPosition.h */; };
 		1D6ED85919AEA20D005A7799 /* FindContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D53FD13181C4B4B00524D4F /* FindContext.h */; };
 		1D6ED85A19AEA20D005A7799 /* ProfilesTerminalPreferencesViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6A2698D1900F95D00437DA9 /* ProfilesTerminalPreferencesViewController.h */; };
 		1D6ED85C19AEA20D005A7799 /* iTermPreferencesBaseViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6E7138718F26445008D94DD /* iTermPreferencesBaseViewController.h */; };
 		1D6ED85D19AEA20D005A7799 /* ITAddressBookMgr.h in Headers */ = {isa = PBXBuildFile; fileRef = 20E74F4804E9089700000106 /* ITAddressBookMgr.h */; };
@@ -2015,11 +2014,10 @@
 		A6C762FF1B45C52B00E3C992 /* TransferrableFile.m in Sources */ = {isa = PBXBuildFile; fileRef = A68A30D7186D1429007F550F /* TransferrableFile.m */; };
 		A6C763001B45C52B00E3C992 /* TransferrableFileMenuItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = A68A30D8186D1429007F550F /* TransferrableFileMenuItemView.m */; };
 		A6C763011B45C52B00E3C992 /* TransferrableFileMenuItemViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A68A30D9186D1429007F550F /* TransferrableFileMenuItemViewController.m */; };
 		A6C763021B45C52B00E3C992 /* VT100RemoteHost.m in Sources */ = {isa = PBXBuildFile; fileRef = A68A30DA186D1429007F550F /* VT100RemoteHost.m */; };
 		A6C763031B45C52B00E3C992 /* VT100WorkingDirectory.m in Sources */ = {isa = PBXBuildFile; fileRef = A68A30DB186D1429007F550F /* VT100WorkingDirectory.m */; };
-		A6C763051B45C52B00E3C992 /* AsyncHostLookupController.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CFDAD6185D53C2005DC94B /* AsyncHostLookupController.m */; };
 		A6C763061B45C52B00E3C992 /* BackgroundThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DA26ABF15007507004B5792 /* BackgroundThread.m */; };
 		A6C763071B45C52B00E3C992 /* CapturedOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = A67F57D31B11882900B4F135 /* CapturedOutput.m */; };
 		A6C763091B45C52B00E3C992 /* iTermCommandHistoryCommandUseMO+Addtions.m in Sources */ = {isa = PBXBuildFile; fileRef = A6E7474C188C6394005355CF /* iTermCommandHistoryCommandUseMO+Addtions.m */; };
 		A6C7630A1B45C52B00E3C992 /* FindContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D53FD14181C4B4B00524D4F /* FindContext.m */; };
 		A6C7630B1B45C52B00E3C992 /* FontSizeEstimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DA8117D13CEA30A00CCA89A /* FontSizeEstimator.m */; };
@@ -2315,11 +2313,10 @@
 		A6CEDA5F19988A7300BB57EC /* LogoFrame.png in Resources */ = {isa = PBXBuildFile; fileRef = A6CEDA5D19988A7300BB57EC /* LogoFrame.png */; };
 		A6CEDA6019988A7300BB57EC /* LogoFrame.png in Resources */ = {isa = PBXBuildFile; fileRef = A6CEDA5D19988A7300BB57EC /* LogoFrame.png */; };
 		A6CEDA6119988A7300BB57EC /* LogoShadow.png in Resources */ = {isa = PBXBuildFile; fileRef = A6CEDA5E19988A7300BB57EC /* LogoShadow.png */; };
 		A6CEDA6219988A7300BB57EC /* LogoShadow.png in Resources */ = {isa = PBXBuildFile; fileRef = A6CEDA5E19988A7300BB57EC /* LogoShadow.png */; };
 		A6CFDAD2185D2587005DC94B /* URLAction.h in Headers */ = {isa = PBXBuildFile; fileRef = A6CFDAD0185D2587005DC94B /* URLAction.h */; };
-		A6CFDAD7185D53C2005DC94B /* AsyncHostLookupController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */; };
 		A6D22A431BC8BE6B004084E0 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = A6D22A411BC8BE6B004084E0 /* Model.xcdatamodeld */; };
 		A6D22A441BC8BE6B004084E0 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = A6D22A411BC8BE6B004084E0 /* Model.xcdatamodeld */; };
 		A6D22A451BC8BE6B004084E0 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = A6D22A411BC8BE6B004084E0 /* Model.xcdatamodeld */; };
 		A6D22B441BC9D368004084E0 /* iTermShellHistoryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A6D22B431BC9D368004084E0 /* iTermShellHistoryTest.m */; };
 		A6DF401C1897607E00F05947 /* NSTextField+iTerm.h in Headers */ = {isa = PBXBuildFile; fileRef = A6DF401A1897607E00F05947 /* NSTextField+iTerm.h */; };
@@ -3835,12 +3832,10 @@
 		A6CEC0DB1DCE812F009F4FD2 /* GPBWireFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPBWireFormat.h; path = ThirdParty/ProtobufRuntime/GPBWireFormat.h; sourceTree = SOURCE_ROOT; };
 		A6CEDA5D19988A7300BB57EC /* LogoFrame.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = LogoFrame.png; path = images/LogoParts/LogoFrame.png; sourceTree = "<group>"; };
 		A6CEDA5E19988A7300BB57EC /* LogoShadow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = LogoShadow.png; path = images/LogoParts/LogoShadow.png; sourceTree = "<group>"; };
 		A6CFDAD0185D2587005DC94B /* URLAction.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = URLAction.h; sourceTree = "<group>"; tabWidth = 4; };
 		A6CFDAD1185D2587005DC94B /* URLAction.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = URLAction.m; sourceTree = "<group>"; tabWidth = 4; };
-		A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = AsyncHostLookupController.h; sourceTree = "<group>"; tabWidth = 4; };
-		A6CFDAD6185D53C2005DC94B /* AsyncHostLookupController.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncHostLookupController.m; sourceTree = "<group>"; tabWidth = 4; };
 		A6D22A421BC8BE6B004084E0 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
 		A6D22B431BC9D368004084E0 /* iTermShellHistoryTest.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = iTermShellHistoryTest.m; sourceTree = "<group>"; };
 		A6DF401A1897607E00F05947 /* NSTextField+iTerm.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextField+iTerm.h"; sourceTree = "<group>"; tabWidth = 4; };
 		A6DF401B1897607E00F05947 /* NSTextField+iTerm.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextField+iTerm.m"; sourceTree = "<group>"; tabWidth = 4; };
 		A6DF7B711A5BA33900D96409 /* iTermParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iTermParser.h; sourceTree = "<group>"; };
@@ -4200,11 +4195,10 @@
 				A6E713B018FCB559008D94DD /* AdvancedWorkingDirectoryWindowController.h */,
 				1D9DCC14142D7FC10016228A /* AlertTrigger.h */,
 				A66DB8341C8E4CBB00233E88 /* iTermOpenQuicklyCommands.h */,
 				A6E7138C18F26A91008D94DD /* AppearancePreferencesViewController.h */,
 				1D2560A813EE60E4006B35CD /* ArrangementPreviewView.h */,
-				A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */,
 				1DE214DF128212EE004E3ADF /* Autocomplete.h */,
 				1DA26ABE15007507004B5792 /* BackgroundThread.h */,
 				1D9DCC0C142D7F5F0016228A /* BellTrigger.h */,
 				1D9DCC08142D7F300016228A /* BounceTrigger.h */,
 				A6E7139918F7B199008D94DD /* BulkCopyProfilePreferencesWindowController.h */,
@@ -5628,11 +5622,10 @@
 			children = (
 				A6E761621D39D216005C0E5C /* iTermMutableAttributedStringBuilder.h */,
 				A6E761631D39D216005C0E5C /* iTermMutableAttributedStringBuilder.m */,
 				539325EE1DD66EF100CABCEF /* iTermPreciseTimer.h */,
 				539325EF1DD66EF100CABCEF /* iTermPreciseTimer.m */,
-				A6CFDAD6185D53C2005DC94B /* AsyncHostLookupController.m */,
 				1DA26ABF15007507004B5792 /* BackgroundThread.m */,
 				A67F57D31B11882900B4F135 /* CapturedOutput.m */,
 				1D53FD14181C4B4B00524D4F /* FindContext.m */,
 				1DA8117D13CEA30A00CCA89A /* FontSizeEstimator.m */,
 				A67F57BD1B01A08800B4F135 /* iTermAnimatedImageInfo.m */,
@@ -6132,11 +6125,10 @@
 		1D6ED85519AEA20D005A7799 /* Headers */ = {
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				1D6ED85619AEA20D005A7799 /* URLAction.h in Headers */,
-				1D6ED85719AEA20D005A7799 /* AsyncHostLookupController.h in Headers */,
 				1D6ED85819AEA20D005A7799 /* LineBufferPosition.h in Headers */,
 				1D6ED85919AEA20D005A7799 /* FindContext.h in Headers */,
 				1D6ED85A19AEA20D005A7799 /* ProfilesTerminalPreferencesViewController.h in Headers */,
 				1D6ED85C19AEA20D005A7799 /* iTermPreferencesBaseViewController.h in Headers */,
 				1D6ED85D19AEA20D005A7799 /* ITAddressBookMgr.h in Headers */,
@@ -6476,11 +6468,10 @@
 		874206480564169600CFC3F1 /* Headers */ = {
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				A6CFDAD2185D2587005DC94B /* URLAction.h in Headers */,
-				A6CFDAD7185D53C2005DC94B /* AsyncHostLookupController.h in Headers */,
 				1D78B55E183EE1C000014D49 /* LineBufferPosition.h in Headers */,
 				A65B72781B2BF2D200F947A7 /* iTermProfilesPanel.h in Headers */,
 				1D53FD15181C4B4B00524D4F /* FindContext.h in Headers */,
 				A6A269901900F95D00437DA9 /* ProfilesTerminalPreferencesViewController.h in Headers */,
 				A6E7138918F26445008D94DD /* iTermPreferencesBaseViewController.h in Headers */,
@@ -8483,11 +8474,10 @@
 				A6CEC10E1DCE8146009F4FD2 /* GPBMessage.m in Sources */,
 				A6C763271B45C52B00E3C992 /* PointerController.m in Sources */,
 				A6C7637C1B45C52B00E3C992 /* ProfilesColorsPreferencesViewController.m in Sources */,
 				A6C763E21B45C6DD00E3C992 /* PSMTabDragWindow.m in Sources */,
 				A6936B5A1D2F5D1A00521B04 /* iTermHotkeyPreferencesModel.m in Sources */,
-				A6C763051B45C52B00E3C992 /* AsyncHostLookupController.m in Sources */,
 				A6C763DA1B45C6DD00E3C992 /* NSWindow+PSM.m in Sources */,
 				A6C763121B45C52B00E3C992 /* iTermImageInfo.m in Sources */,
 				A6C7636E1B45C52B00E3C992 /* iTermPreferences.m in Sources */,
 				A6C763DB1B45C6DD00E3C992 /* PSMDarkTabStyle.m in Sources */,
 				A6C763B01B45C52B00E3C992 /* GrowlTrigger.m in Sources */,
diff --git a/sources/AsyncHostLookupController.h b/sources/AsyncHostLookupController.h
deleted file mode 100644
index 0e022f2a..00000000
--- a/sources/AsyncHostLookupController.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//
-//  AsyncHostLookupController.h
-//  iTerm
-//
-//  Created by George Nachman on 12/14/13.
-//
-//
-
-#import <Foundation/Foundation.h>
-
-// Verify whether host names are valid. Runs in a separate thread and provides asynchronous results.
-// Caches very aggressively.
-@interface AsyncHostLookupController : NSObject
-
-+ (instancetype)sharedInstance;
-
-// Calls back to completion indicating whether |host| is an extant hostname. The BOOL is YES if the
-// hostname resolves. |host| is passed as the second argument to completion. Does not block.
-- (void)getAddressForHost:(NSString *)host
-               completion:(void (^)(BOOL, NSString*))completion;
-
-// Cancels the lookup for |hostname|.
-- (void)cancelRequestForHostname:(NSString *)hostname;
-
-@end
diff --git a/sources/AsyncHostLookupController.m b/sources/AsyncHostLookupController.m
deleted file mode 100644
index a82675de..00000000
--- a/sources/AsyncHostLookupController.m
+++ /dev/null
@@ -1,119 +0,0 @@
-//
-//  AsyncHostLookupController.m
-//  iTerm
-//
-//  Created by George Nachman on 12/14/13.
-//
-//
-
-#import "AsyncHostLookupController.h"
-#import "DebugLogging.h"
-#import "iTermAdvancedSettingsModel.h"
-#include <netdb.h>
-
-@implementation AsyncHostLookupController {
-    // Created at initialization and used to perform blocking gethostbyname calls.
-    dispatch_queue_t _queue;
-
-    // Set of hostnames waiting to be looked up. If a hostname is removed from
-    // this set then it won't be looked up when its turn comes around.
-    NSMutableSet *_pending;
-
-    // Maps hostname -> @YES or @NO, indicating if it resolved.
-    NSMutableDictionary *_cache;
-}
-
-+ (instancetype)sharedInstance {
-    static AsyncHostLookupController *instance;
-    static dispatch_once_t once;
-    dispatch_once(&once, ^{
-        instance = [[self alloc] init];
-    });
-    return instance;
-}
-
-- (instancetype)init {
-    self = [super init];
-    if (self) {
-        _queue = dispatch_queue_create("AsyncHostLookupControllerQueue", NULL);
-        _pending = [[NSMutableSet alloc] init];
-        _cache = [[NSMutableDictionary alloc] init];
-    }
-    return self;
-}
-
-- (void)dealloc {
-    // The logic for cleaning up the dispatch queue isn't written, so just make sure the singleton
-    // never gets dealloced.
-    assert(false);
-    [super dealloc];
-}
-
-- (void)getAddressForHost:(NSString *)hostname
-               completion:(void (^)(BOOL, NSString *))completion {
-    if (![iTermAdvancedSettingsModel performDNSLookups]) {
-        completion(YES, hostname);
-        return;
-    }
-
-    NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
-    @synchronized(self) {
-        if ([_pending containsObject:hostname]) {
-            DLog(@"Already pending %@", hostname);
-            return;
-        }
-        NSNumber *okNumber = _cache[hostname];
-        if (okNumber) {
-            completion([okNumber boolValue], hostname);
-            return;
-        }
-        [_pending addObject:hostname];
-    }
-    dispatch_async(_queue, ^() {
-        @synchronized(self) {
-            if (![_pending containsObject:hostname]) {
-                DLog(@"Abort nslookup for %@", hostname);
-                return;
-            }
-        }
-
-        BOOL shouldCache;
-        BOOL ok = [self hostnameHasAddress:[hostname UTF8String] shouldCache:&shouldCache];
-        if (shouldCache) {
-            @synchronized(self) {
-                _cache[hostname] = @(ok);
-            }
-        }
-        dispatch_async(dispatch_get_main_queue(), ^() {
-            @synchronized(self) {
-                if (![_pending containsObject:hostname]) {
-                    DLog(@"Finished nslookup but don't call block for %@", hostname);
-                    return;
-                }
-                [_pending removeObject:hostname];
-            }
-            DLog(@"Host %@: %@", hostname, ok ? @"OK" : @"Unknown");
-            completion(ok, hostname);
-        });
-    });
-    DLog(@"Blocked main thread for %f sec", [NSDate timeIntervalSinceReferenceDate] - start);
-}
-
-- (void)cancelRequestForHostname:(NSString *)hostname {
-    @synchronized(self) {
-        [_pending removeObject:hostname];
-    }
-}
-
-- (BOOL)hostnameHasAddress:(const char *)hostname shouldCache:(BOOL *)shouldCache {
-    struct addrinfo *servinfo;
-    int status = getaddrinfo(hostname, NULL, NULL, &servinfo);
-    if (status == 0) {
-        freeaddrinfo(servinfo);
-    }
-    // Don't cache temporary failures
-    *shouldCache = (status != EAI_AGAIN);
-    return status == 0;
-}
-
-@end
diff --git a/sources/PTYTextView.m b/sources/PTYTextView.m
index ded180f1..48f85b48 100644
--- a/sources/PTYTextView.m
+++ b/sources/PTYTextView.m
@@ -1,8 +1,7 @@
 #import "PTYTextView.h"
 
-#import "AsyncHostLookupController.h"
 #import "charmaps.h"
 #import "FileTransferManager.h"
 #import "FontSizeEstimator.h"
 #import "FutureMethods.h"
 #import "FutureMethods.h"
@@ -94,14 +93,10 @@
 
 static const NSUInteger kDragPaneModifiers = (NSAlternateKeyMask | NSCommandKeyMask | NSShiftKeyMask);
 static const NSUInteger kRectangularSelectionModifiers = (NSCommandKeyMask | NSAlternateKeyMask);
 static const NSUInteger kRectangularSelectionModifierMask = (kRectangularSelectionModifiers | NSControlKeyMask);
 
-// Notifications posted when hostname lookups finish. Notifications are used to
-// avoid dangling references.
-static NSString *const kHostnameLookupFailed = @"kHostnameLookupFailed";
-static NSString *const kHostnameLookupSucceeded = @"kHostnameLookupSucceeded";
 static PTYTextView *gCurrentKeyEventTextView;  // See comment in -keyDown:
 
 // Minimum distance that the mouse must move before a cmd+drag will be
 // recognized as a drag.
 static const int kDragThreshold = 3;
@@ -117,13 +112,10 @@ @interface PTYTextView () <
     iTermSelectionScrollHelperDelegate,
     NSDraggingSource,
     NSMenuDelegate,
     NSPopoverDelegate>
 
-// Set the hostname this view is currently waiting for AsyncHostLookupController to finish looking
-// up.
-@property(nonatomic, copy) NSString *currentUnderlineHostname;
 @property(nonatomic, retain) iTermSelection *selection;
 @property(nonatomic, retain) iTermSemanticHistoryController *semanticHistoryController;
 @property(nonatomic, retain) iTermFindCursorView *findCursorView;
 @property(nonatomic, retain) NSWindow *findCursorWindow;  // For find-cursor animation
 @property(nonatomic, retain) iTermQuickLookController *quickLookController;
@@ -307,18 +299,10 @@ - (instancetype)initWithFrame:(NSRect)aRect colorMap:(iTermColorMap *)colorMap {
                                                    object:nil];
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(_pointerSettingsChanged:)
                                                      name:kPointerPrefsChangedNotification
                                                    object:nil];
-        [[NSNotificationCenter defaultCenter] addObserver:self
-                                                 selector:@selector(hostnameLookupFailed:)
-                                                     name:kHostnameLookupFailed
-                                                   object:nil];
-        [[NSNotificationCenter defaultCenter] addObserver:self
-                                                 selector:@selector(hostnameLookupSucceeded:)
-                                                     name:kHostnameLookupSucceeded
-                                                   object:nil];
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(imageDidLoad:)
                                                      name:iTermImageDidLoad
                                                    object:nil];
         
@@ -398,14 +382,10 @@ - (void)dealloc {
     [pointer_ release];
     [cursor_ release];
     [threeFingerTapGestureRecognizer_ disconnectTarget];
     [threeFingerTapGestureRecognizer_ release];
 
-    if (self.currentUnderlineHostname) {
-        [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
-    }
-    [_currentUnderlineHostname release];
     _indicatorsHelper.delegate = nil;
     [_indicatorsHelper release];
     _selectionScrollHelper.delegate = nil;
     [_selectionScrollHelper release];
     _findOnPageHelper.delegate = nil;
@@ -1113,11 +1093,10 @@ - (void)drawRect:(NSRect)rect {
     _drawingHelper.textViewIsActiveSession = [self.delegate textViewIsActiveSession];
     _drawingHelper.isInKeyWindow = [self isInKeyWindow];
     // Draw the cursor filled in when we're inactive if there's a popup open or key focus was stolen.
     _drawingHelper.shouldDrawFilledInCursor = ([self.delegate textViewShouldDrawFilledInCursor] || _keyFocusStolenCount);
     _drawingHelper.isFrontTextView = (self == [[iTermController sharedInstance] frontTextView]);
-    _drawingHelper.haveUnderlinedHostname = (self.currentUnderlineHostname != nil);
     _drawingHelper.transparencyAlpha = [self transparencyAlpha];
     _drawingHelper.now = [NSDate timeIntervalSinceReferenceDate];
     _drawingHelper.drawMarkIndicators = [_delegate textViewShouldShowMarkIndicators];
     _drawingHelper.thinStrokes = _thinStrokes;
     _drawingHelper.showSearchingCursor = _showSearchingCursor;
@@ -1770,14 +1749,10 @@ - (void)removeUnderline {
     if (![self hasUnderline]) {
         return;
     }
     _drawingHelper.underlinedRange =
         VT100GridAbsWindowedRangeMake(VT100GridAbsCoordRangeMake(-1, -1, -1, -1), 0, 0);
-    if (self.currentUnderlineHostname) {
-        [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
-    }
-    self.currentUnderlineHostname = nil;
     [self setNeedsDisplay:YES];  // It would be better to just display the underlined/formerly underlined area.
 }
 
 // Update range of underlined chars indicating cmd-clicakble url.
 - (URLAction *)updateUnderlinedURLs:(NSEvent *)event {
@@ -1807,33 +1782,13 @@ - (URLAction *)updateUnderlinedURLs:(NSEvent *)event {
             if (action) {
                 _drawingHelper.underlinedRange = VT100GridAbsWindowedRangeFromRelative(action.range, [_dataSource totalScrollbackOverflow]);
 
                 if (action.actionType == kURLActionOpenURL) {
                     NSURL *url = [NSURL URLWithUserSuppliedString:action.string];
-                    if (![url.host isEqualToString:self.currentUnderlineHostname]) {
-                        if (self.currentUnderlineHostname) {
-                            [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
-                        }
-                        if (url && url.host) {
-                            self.currentUnderlineHostname = url.host;
-                            [[AsyncHostLookupController sharedInstance] getAddressForHost:url.host
-                                                                               completion:^(BOOL ok, NSString *hostname) {
-                                                                                   if (!ok) {
-                                                                                       [[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupFailed
-                                                                                                                                           object:hostname];
-                                                                                   } else {
-                                                                                       [[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupSucceeded
-                                                                                                                                           object:hostname];
-                                                                                   }
-                                                                               }];
-                        }
+                    if (url && url.host) {
+                        [self setNeedsDisplay:YES];
                     }
-                } else {
-                    if (self.currentUnderlineHostname) {
-                        [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
-                    }
-                    self.currentUnderlineHostname = nil;
                 }
             } else {
                 [self removeUnderline];
                 return action;
             }
@@ -6186,27 +6141,10 @@ - (URLAction *)urlActionForClickAtX:(int)x
                                        pathFactory:^SCPPath *(NSString *path, int line) {
                                            return [_dataSource scpPathForFile:path onLine:line];
                                        }];
 }
 
-- (void)hostnameLookupFailed:(NSNotification *)notification {
-    if ([[notification object] isEqualToString:self.currentUnderlineHostname]) {
-        self.currentUnderlineHostname = nil;
-        [self removeUnderline];
-        _drawingHelper.underlinedRange =
-            VT100GridAbsWindowedRangeMake(VT100GridAbsCoordRangeMake(-1, -1, -1, -1), 0, 0);
-        [self setNeedsDisplay:YES];
-    }
-}
-
-- (void)hostnameLookupSucceeded:(NSNotification *)notification {
-    if ([[notification object] isEqualToString:self.currentUnderlineHostname]) {
-        self.currentUnderlineHostname = nil;
-        [self setNeedsDisplay:YES];
-    }
-}
-
 - (void)imageDidLoad:(NSNotification *)notification {
     if ([self imageIsVisible:notification.object]) {
         [self setNeedsDisplay:YES];
     }
 }
diff --git a/sources/iTermAdvancedSettingsModel.h b/sources/iTermAdvancedSettingsModel.h
index 86514071..98f1e9af 100644
--- a/sources/iTermAdvancedSettingsModel.h
+++ b/sources/iTermAdvancedSettingsModel.h
@@ -159,11 +159,10 @@
 + (int)terminalMargin;
 + (int)terminalVMargin;
 + (BOOL)useColorfgbgFallback;
 + (BOOL)promptForPasteWhenNotAtPrompt;
 + (void)setPromptForPasteWhenNotAtPrompt:(BOOL)value;
-+ (BOOL)performDNSLookups;
 + (BOOL)zeroWidthSpaceAdvancesCursor;
 + (BOOL)darkThemeHasBlackTitlebar;
 + (BOOL)fontChangeAffectsBroadcastingSessions;
 + (BOOL)zippyTextDrawing;
 + (BOOL)noSyncSuppressClipboardAccessDeniedWarning;
diff --git a/sources/iTermAdvancedSettingsModel.m b/sources/iTermAdvancedSettingsModel.m
index 91a948ba..1b23bf5d 100644
--- a/sources/iTermAdvancedSettingsModel.m
+++ b/sources/iTermAdvancedSettingsModel.m
@@ -180,11 +180,10 @@ + (NSString *)name { \
 DEFINE_BOOL(ignoreHardNewlinesInURLs, NO, @"Semantic History: Ignore hard newlines for the purposes of locating URLs and file names for Semantic History.\nIf a hard newline occurs at the end of a line then cmd-click will not see it all unless this setting is turned on. This is useful for some interactive applications. Turning this on will remove newlines from the \\3 and \\4 substitutions.");
 // Note: square brackets are included for ipv6 addresses like http://[2600:3c03::f03c:91ff:fe96:6a7a]/
 DEFINE_STRING(URLCharacterSet, @".?\\/:;%=&_-,+~#@!*'(（)）|[]", @"Semantic History: Non-alphanumeric characters considered part of a URL for Semantic History.\nLetters and numbers are always considered part of the URL. These non-alphanumeric characters are used in addition for the purposes of figuring out where a URL begins and ends.");
 DEFINE_INT(maxSemanticHistoryPrefixOrSuffix, 2000, @"Semantic History: Maximum number of bytes of text before and after click location to take into account.\nThis also limits the size of the \\3 and \\4 substitutions.");
 DEFINE_STRING(pathsToIgnore, @"", @"Semantic History: Paths to ignore for Semantic History.\nSeparate paths with a comma. Any file under one of these paths will not be openable with Semantic History.");
-DEFINE_BOOL(performDNSLookups, NO, @"Semantic History: Perform DNS lookups to check if URLs are valid?\nWhen enabled, the name under the mouse will be resolved with DNS to determine if it is a clickable link.");
 DEFINE_BOOL(showYellowMarkForJobStoppedBySignal, YES, @"Semantic History: Use a yellow for a Shell Integration prompt mark when the job is stopped by a signal.");
 
 #pragma mark - Debugging
 DEFINE_BOOL(startDebugLoggingAutomatically, NO, @"Debugging: Start debug logging automatically when iTerm2 is launched.");
 DEFINE_BOOL(logDrawingPerformance, NO, @"Debugging: Log stats about text drawing performance to console.\nUsed for performance testing.");
diff --git a/sources/iTermTextDrawingHelper.h b/sources/iTermTextDrawingHelper.h
index eaa34cfe..d4775068 100644
--- a/sources/iTermTextDrawingHelper.h
+++ b/sources/iTermTextDrawingHelper.h
@@ -157,13 +157,10 @@
 
 // Is this the current text view of the "front" terminal window?
 // TODO: This might be the same as textViewIsActiveSession.
 @property(nonatomic, assign) BOOL isFrontTextView;
 
-// Is there an underlined hostname?
-@property(nonatomic, assign) BOOL haveUnderlinedHostname;
-
 // Background transparency level. Should be 1 if transparency is off for this view.
 @property(nonatomic, assign) double transparencyAlpha;
 
 // Is the cursor visible?
 @property(nonatomic, assign) BOOL cursorVisible;
diff --git a/sources/iTermTextDrawingHelper.m b/sources/iTermTextDrawingHelper.m
index ce49fa83..0bee1073 100644
--- a/sources/iTermTextDrawingHelper.m
+++ b/sources/iTermTextDrawingHelper.m
@@ -108,11 +108,10 @@ @interface iTermTextDrawingHelper() <iTermCursorDelegate>
     screen_char_t previousCharacterAttributes;
     BOOL havePreviousCharacterAttributes;
     NSColor *backgroundColor;
     NSColor *previousBackgroundColor;
     CGFloat minimumContrast;
-    BOOL haveUnderlinedHostname;
     NSColor *previousForegroundColor;
 } iTermTextColorContext;
 
 @implementation iTermTextDrawingHelper {
     // Current font. Only valid for the duration of a single drawing context.
@@ -1553,11 +1552,11 @@ - (CGContextRef)newGrayscaleContextOfSize:(NSSize)size {
 
     if (isMatch) {
         // Black-on-yellow search result.
         rawColor = [NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1];
         context->havePreviousCharacterAttributes = NO;
-    } else if (inUnderlinedRange && !context->haveUnderlinedHostname) {
+    } else if (inUnderlinedRange) {
         // Blue link text.
         rawColor = [context->colorMap colorForKey:kColorMapLink];
         context->havePreviousCharacterAttributes = NO;
     } else if (context->hasSelectedText) {
         // Selected text.
@@ -1863,11 +1862,10 @@ - (BOOL)zippy {
         .findMatches = findMatches,
         .reverseVideo = _reverseVideo,
         .havePreviousCharacterAttributes = NO,
         .backgroundColor = backgroundColor,
         .minimumContrast = _minimumContrast,
-        .haveUnderlinedHostname = _haveUnderlinedHostname,
         .previousForegroundColor = nil,
     };
     NSDictionary *previousImageAttributes = nil;
     iTermMutableAttributedStringBuilder *builder = [[[iTermMutableAttributedStringBuilder alloc] init] autorelease];
     builder.zippy = self.zippy;

Vulnerable code: 		1D6ED85719AEA20D005A7799 /* AsyncHostLookupController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */; };
		A6C763051B45C52B00E3C992 /* AsyncHostLookupController.m in Sources */ = {isa = PBXBuildFile; fileRef = A6CFDAD6185D53C2005DC94B /* AsyncHostLookupController.m */; };
		A6CFDAD7185D53C2005DC94B /* AsyncHostLookupController.h in Headers */ = {isa = PBXBuildFile; fileRef = A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */; };
		A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = AsyncHostLookupController.h; sourceTree = "<group>"; tabWidth = 4; };
		A6CFDAD6185D53C2005DC94B /* AsyncHostLookupController.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncHostLookupController.m; sourceTree = "<group>"; tabWidth = 4; };
				A6CFDAD5185D53C2005DC94B /* AsyncHostLookupController.h */,
				A6CFDAD6185D53C2005DC94B /* AsyncHostLookupController.m */,
				1D6ED85719AEA20D005A7799 /* AsyncHostLookupController.h in Headers */,
				A6CFDAD7185D53C2005DC94B /* AsyncHostLookupController.h in Headers */,
				A6C763051B45C52B00E3C992 /* AsyncHostLookupController.m in Sources */,
//
//  AsyncHostLookupController.h
//  iTerm
//
//  Created by George Nachman on 12/14/13.
//
//

#import <Foundation/Foundation.h>

// Verify whether host names are valid. Runs in a separate thread and provides asynchronous results.
// Caches very aggressively.
@interface AsyncHostLookupController : NSObject

+ (instancetype)sharedInstance;

// Calls back to completion indicating whether |host| is an extant hostname. The BOOL is YES if the
// hostname resolves. |host| is passed as the second argument to completion. Does not block.
- (void)getAddressForHost:(NSString *)host
               completion:(void (^)(BOOL, NSString*))completion;

// Cancels the lookup for |hostname|.
- (void)cancelRequestForHostname:(NSString *)hostname;

@end
//
//  AsyncHostLookupController.m
//  iTerm
//
//  Created by George Nachman on 12/14/13.
//
//

#import "AsyncHostLookupController.h"
#import "DebugLogging.h"
#import "iTermAdvancedSettingsModel.h"
#include <netdb.h>

@implementation AsyncHostLookupController {
    // Created at initialization and used to perform blocking gethostbyname calls.
    dispatch_queue_t _queue;

    // Set of hostnames waiting to be looked up. If a hostname is removed from
    // this set then it won't be looked up when its turn comes around.
    NSMutableSet *_pending;

    // Maps hostname -> @YES or @NO, indicating if it resolved.
    NSMutableDictionary *_cache;
}

+ (instancetype)sharedInstance {
    static AsyncHostLookupController *instance;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _queue = dispatch_queue_create("AsyncHostLookupControllerQueue", NULL);
        _pending = [[NSMutableSet alloc] init];
        _cache = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void)dealloc {
    // The logic for cleaning up the dispatch queue isn't written, so just make sure the singleton
    // never gets dealloced.
    assert(false);
    [super dealloc];
}

- (void)getAddressForHost:(NSString *)hostname
               completion:(void (^)(BOOL, NSString *))completion {
    if (![iTermAdvancedSettingsModel performDNSLookups]) {
        completion(YES, hostname);
        return;
    }

    NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
    @synchronized(self) {
        if ([_pending containsObject:hostname]) {
            DLog(@"Already pending %@", hostname);
            return;
        }
        NSNumber *okNumber = _cache[hostname];
        if (okNumber) {
            completion([okNumber boolValue], hostname);
            return;
        }
        [_pending addObject:hostname];
    }
    dispatch_async(_queue, ^() {
        @synchronized(self) {
            if (![_pending containsObject:hostname]) {
                DLog(@"Abort nslookup for %@", hostname);
                return;
            }
        }

        BOOL shouldCache;
        BOOL ok = [self hostnameHasAddress:[hostname UTF8String] shouldCache:&shouldCache];
        if (shouldCache) {
            @synchronized(self) {
                _cache[hostname] = @(ok);
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^() {
            @synchronized(self) {
                if (![_pending containsObject:hostname]) {
                    DLog(@"Finished nslookup but don't call block for %@", hostname);
                    return;
                }
                [_pending removeObject:hostname];
            }
            DLog(@"Host %@: %@", hostname, ok ? @"OK" : @"Unknown");
            completion(ok, hostname);
        });
    });
    DLog(@"Blocked main thread for %f sec", [NSDate timeIntervalSinceReferenceDate] - start);
}

- (void)cancelRequestForHostname:(NSString *)hostname {
    @synchronized(self) {
        [_pending removeObject:hostname];
    }
}

- (BOOL)hostnameHasAddress:(const char *)hostname shouldCache:(BOOL *)shouldCache {
    struct addrinfo *servinfo;
    int status = getaddrinfo(hostname, NULL, NULL, &servinfo);
    if (status == 0) {
        freeaddrinfo(servinfo);
    }
    // Don't cache temporary failures
    *shouldCache = (status != EAI_AGAIN);
    return status == 0;
}

@end
#import "AsyncHostLookupController.h"
// Notifications posted when hostname lookups finish. Notifications are used to
// avoid dangling references.
static NSString *const kHostnameLookupFailed = @"kHostnameLookupFailed";
static NSString *const kHostnameLookupSucceeded = @"kHostnameLookupSucceeded";
// Set the hostname this view is currently waiting for AsyncHostLookupController to finish looking
// up.
@property(nonatomic, copy) NSString *currentUnderlineHostname;
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(hostnameLookupFailed:)
                                                     name:kHostnameLookupFailed
                                                   object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(hostnameLookupSucceeded:)
                                                     name:kHostnameLookupSucceeded
                                                   object:nil];
    if (self.currentUnderlineHostname) {
        [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
    }
    [_currentUnderlineHostname release];
    _drawingHelper.haveUnderlinedHostname = (self.currentUnderlineHostname != nil);
    if (self.currentUnderlineHostname) {
        [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
    }
    self.currentUnderlineHostname = nil;
                    if (![url.host isEqualToString:self.currentUnderlineHostname]) {
                        if (self.currentUnderlineHostname) {
                            [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
                        }
                        if (url && url.host) {
                            self.currentUnderlineHostname = url.host;
                            [[AsyncHostLookupController sharedInstance] getAddressForHost:url.host
                                                                               completion:^(BOOL ok, NSString *hostname) {
                                                                                   if (!ok) {
                                                                                       [[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupFailed
                                                                                                                                           object:hostname];
                                                                                   } else {
                                                                                       [[NSNotificationCenter defaultCenter] postNotificationName:kHostnameLookupSucceeded
                                                                                                                                           object:hostname];
                                                                                   }
                                                                               }];
                        }
                } else {
                    if (self.currentUnderlineHostname) {
                        [[AsyncHostLookupController sharedInstance] cancelRequestForHostname:self.currentUnderlineHostname];
                    }
                    self.currentUnderlineHostname = nil;
- (void)hostnameLookupFailed:(NSNotification *)notification {
    if ([[notification object] isEqualToString:self.currentUnderlineHostname]) {
        self.currentUnderlineHostname = nil;
        [self removeUnderline];
        _drawingHelper.underlinedRange =
            VT100GridAbsWindowedRangeMake(VT100GridAbsCoordRangeMake(-1, -1, -1, -1), 0, 0);
        [self setNeedsDisplay:YES];
    }
}

- (void)hostnameLookupSucceeded:(NSNotification *)notification {
    if ([[notification object] isEqualToString:self.currentUnderlineHostname]) {
        self.currentUnderlineHostname = nil;
        [self setNeedsDisplay:YES];
    }
}

+ (BOOL)performDNSLookups;
DEFINE_BOOL(performDNSLookups, NO, @"Semantic History: Perform DNS lookups to check if URLs are valid?\nWhen enabled, the name under the mouse will be resolved with DNS to determine if it is a clickable link.");
// Is there an underlined hostname?
@property(nonatomic, assign) BOOL haveUnderlinedHostname;

    BOOL haveUnderlinedHostname;
    } else if (inUnderlinedRange && !context->haveUnderlinedHostname) {
        .haveUnderlinedHostname = _haveUnderlinedHostname,
Fixed code:                     if (url && url.host) {
                        [self setNeedsDisplay:YES];
    } else if (inUnderlinedRange) {