• This repository has been archived on 06/Feb/2022
  • Stars
    star
    136
  • Rank 267,670 (Top 6 %)
  • Language
    Swift
  • Created over 8 years ago
  • Updated almost 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

DNS Tunneling Client for iOS

Abstract

We designed and implemented a service that provides one-tap DNS tunneling to users. To achieve this, we built both an iPhone app and a DNS server that relays user traffic. We received an entitlement to use Apple’s new Network Extension API, and used it to create a one-button app that configures the iPhone’s operating system to tunnel all system traffic over DNS. We designed an abstraction layer for communicating arbitrary amounts of data between a client and a DNS server, and a packet forwarding protocol that is built atop it. We were successful in implementing all of the various components of the system, but were not able to successfully integrate the Network Extension API due to time constraints.

Introduction

Though the world wide web is meant to be completely open and accessible, this is sometimes not the case when using certain networks that restrict access to the internet. Examples of these restrictions include paywalled WiFi, blocked websites on school networks, and censorship of some or all international sites in countries ruled by oppressive governments. Bypassing these obstacles is commonly achieved by tunneling network traffic around the filtering mechanism to another device that has unrestricted Internet access. This is only possible when the filter or obstacle does not detect that traffic is being routed around it. SSH forwarding and VPNs are common examples of tunneling techniques, but can be detected and due to their prevalence are often blocked. DNS tunneling is an unusual and hard-to-detect method of bypassing network barriers by routing all network traffic through the DNS system. In this project, we investigate the feasibility of creating an easy-to-use DNS tunneling solution for a mobile device.

The biggest and most notable network barrier in use today is China’s Great Firewall. Common tools for circumventing the GFW are proxies, VPNs, and SSH tunneling. Proxies are an unreliable option since they are usually identified and blocked fairly quickly. VPNs use dedicated ports that the government can block completely without disrupting other traffic, and VPN communication with foreign IP addresses can be detected and blocked relatively easily. SSH tunneling used to be a reliable way to bypass the firewall, but the government has been recently cracking down on it. SSH tunneling is encrypted in such a way that the GFW usually cannot determine with certainty if it is tunneling illegal traffic. As a result, the government has recently been preemptively tampering with SSH tunnels that they believe might be performing illegal operations. Thus, the game of cat and mouse continues, and new ways of bypassing these firewalls are necessary.

Motivation

The DNS system provides a promising alternative to traditional tunneling channels. Tunneling over the DNS protocol is difficult to detect and filter, and information transferred across this protocol is crucial to the functionality of the Internet so it cannot be blocked entirely. Furthermore, tampering with DNS traffic can have major side-effects, and relatively long cache times can result in problems caused by tampered data persisting for long periods of time. Perhaps most importantly, DNS tunneling is currently not commonly used as a way of bypassing Internet filters. As a result, it is usually not worth the complication and expense of applying the advanced statistical monitoring techniques required to detect DNS tunneling traffic and the risk of attempting to block it.

There are a number of DNS tunneling programs available. However, most are designed for use on desktop/laptop computers, while the number of mobile Internet users has been increasing at a breakneck pace since 2007, and in 2014 even exceeded the number of desktop Internet users [7]. Furthermore, most of the existing software requires significant technical skill and resources to configure, including operating one’s own server. The vast majority of Internet users aren’t technically skilled enough to set up their own server and don’t have access to the resources to do so - particularly if they live under an oppressive government. Our goal is to create an iOS app that allows a non-technical user to browse the web through a DNS tunnel, including providing our own managed cloud server to relay data.

Related Work

DNS tunneling as a topic of interest seems to have first appeared in 1998 [3]. Since it’s not a subject with much commercial value, and it’s far too tricky for your average hobbyist to successfully set up, there’s not much information about it on the Internet. However, there are a decent number of blog and forum posts floating around, some with snippets of sample code. Most seem to have been written by security professionals interested in the possibility of a virus on an infected system using DNS tunneling to communicate with a C&C server under the radar, and how to defend against such a possibility. Fortunately for us, even professionals guarding highly valuable systems don’t seem to have come up with any strategies for defeating DNS tunneling that are better than statistical analysis and drastic measures like blocking TXT-type DNS responses completely (which breaks important systems that rely on them, like the SPF anti-spam protocol) [1][8].

As mentioned above, there are a significant number of existing projects that seek to provide semi-turnkey DNS tunneling capability on traditional x86 PC’s. Almost all require that users configure and operate their own server, something we identified as an insurmountable barrier to use by a realistic consumer. A few solutions are available for Android, some even paired with a cloud service like ours [2]. However, no solutions exist for iOS as far as we know (without jailbreaking the device, which we assume is another insurmountable barrier to use by an average consumer).

We found a few existing applications that use Apple's newly introduced Network Extension API. For example, iCepa uses the API to route all traffic system-wide through the Tor network [6]. On the app store, we found a few VPN apps that use the API to automatically install profiles (with user permission via a system dialog).

Algorithm Design

To make our protocol easier to reason about, we designed two entirely separate layers of abstraction. The first, which we called the Transmission layer, allows us to communicate arbitrary amounts of data to the server over many DNS lookups and receive an arbitrarily large response. The second, which we called the Session layer and which is built atop the Transmission layer, allows us to forward packets to the server and receive response packets.

The Transmission Layer

This layer was designed to abstract away the complicated and limited nature of two-way communication between a client and a DNS server. The client can only communicate with the server by looking up DNS hostnames of no more than 255 characters total, split into subdomains of no more than 63 characters each. Accordingly, to send a message larger than 255 bytes to the server our client will have to spread it across multiple DNS lookups. The server can respond to a lookup with up to 64KB of information, which we assume is sufficiently large for our purposes. Sending a message to the server requires three steps:

  1. The client will look up begin.burrow.tech and receive a unique ID like 2a591c8b. To defeat caching of the begin endpoint we have to prefix it with some amount of random data, which is simply ignored by the server. (Although TTL values allow some degree of control over the degree of caching, they are insufficient because values cannot be shorter than 1 second and values less than 60 seconds are routinely ignored.)

  2. The client will make several lookups to continue.burrow.tech with three arguments: the transmission ID, the index, and ~220 bytes of data. Having an index allows each of the lookups to be resolved in parallel (and received by the server in any order).

  3. The client looks up end.burrow.tech with two arguments: the transmission ID and the number of continue calls that the server should have received. The server verifies that it has received the correct number of continue calls, concatenates all of the received data according to the indices, processes it somehow (in our case, hands it off to the Session layer), and returns a result to the client.

    .begin.burrow.tech "success=True"; "transmission_id=2a591c8b" this_is_some.0.2a591c8b.continue.burrow.tech sample_data.1.2a591c8b.continue.burrow.tech for_us_to_use.2.2a591c8b.continue.burrow.tech 3.2a591c8b.end.burrow.tech "success=True"; "contents=<______>"

Transmission Diagram

The Session Layer

This layer encapsulates the packet forwarding and polling logic. Session messages are carried over the Transmission layer. A client sets up a session when it starts the Burrow tunnel and uses it to send and receive packets for the lifetime of the connection. Sending and receiving packets happens in 4 steps:

  1. The client begins a session by sending the message b. The server will respond with a new session ID like c8e9471e and a success code (s for success or f for failure).
  2. The client intercepts some packets and forwards them to the server using the forward packet message syntax: f-c8e9471e-<packet1>-<packet2>-.... If the server successfully manages to parse the packets, it will return s.
  3. The client will periodically poll for response packets from the server with the message r-c8e9471e . The server will return any packets it received as responses to previously forwarded packets from this session as s-c8e9471e-<packet1>-<packet2>-...
  4. When the client is ready to disconnect, it will end the session with e-c8e9471e. The server will clean up the resources used to create the session and return s.
message: b
response: s-c8e9471e
message: f-c8e9471e-<packet1>-<packet2>-...
response: s
message: r-c8e9471e
response: s-<packet1>-<packet2>-...
message: e-c8e9471e
response: s

Transmission Diagram

Intercepting Client-Side Network Traffic

In 2015, Apple introduced a series of new extension points with iOS 9, most relevantly NETunnelProvider [4]. An app that provides such an extension must include in its code signature a special entitlement from Apple; otherwise, the operating system will prevent the app from using the tunneling extension point. Unfortunately, in a departure from its approach to most past iOS public APIs, Apple is not making the entitlement required to use this extension point available to any developer that requests it [5]. After going through an application process and a lengthy wait, we received permission to build a network extension. This allowed our app to capture and tunnel all system-wide traffic rather than just our own traffic, ensuring the user can safely use all apps on their device. The NETunnelProvider extension intercepts IP traffic at the network layer, meaning that our app had to be designed for compatibility with both UDP and TCP traffic.

Performing DNS Requests on the Client

A major challenge in performing DNS requests on the client was working with the networking libraries that iOS provides. We initially used the low-level POSIX API since it provided us the most flexibility, such as the ability to specify a desired layer-4 protocol and nameserver, but its synchronous design proved to be a challenge. We considered switching to the 3rd party getdns API, but it provided difficult to integrate into our iOS application. We finally settled on using Apple’s DNS Service Discovery C API, which is intended for zero-configuration networking (Bonjour) but provides sufficient low level functionality for performing TXT queries and was compatible with iOS’s asynchronous runloop paradigm. Spawning a new thread for each DNS lookup with the POSIX API had huge overhead and introduced race conditions. Transitioning to a runloop-based implementation with the Service Discovery API allowed us to asynchronously resolve queries without the complexity of threading.

Fulfilling DNS Requests on the Server

The other half of our project is the cloud service that actually accesses the Internet on behalf of our client devices. This is an always-on Debian 7-based dedicated virtual machine. We used Python and the dnslib library to create an DNS server that returns custom responses to lookups for burrow.tech domains. It handles each incoming lookup as a Transmission-layer message, and once it receives a complete Transmission it handles the data as a Session-layer message. To forward packets, we initially tried to use Python’s socket and asyncore modules, as it would allow us to send and receive packets asynchronously. However, we quickly switched to the ScaPy library, which has convenient functions for spoofing packets and recalculating header checksums, and for capturing response packets for outgoing packets. ScaPy’s send/receive function blocks until it finishes sending packets and receiving the answer packets, so to make the server able to resolve more than one packet at a time, we had to make use of Python’s multiprocessing library to create a thread for every call to the send/receive function.

Layer Stack Diagram

App Design

The iOS app features a single switch to enable and disable the tunnel. When the switch is enabled, an animation occurs as the UI color changes to indicate connectivity. The user can also quickly enable the tunnel from the system Settings app. A VPN indicator displays in the status bar while tunneling is enabled.

App Screenshots

The first time the app is launched, the app requests permission from the user to install a VPN configuration onto the device. The user simply needs to enter their password or use TouchID to authenticate, and then they are really to tunnel. No other setup is necessary.

The app is designed to be very easy to use for users who know nothing about configuring servers. As such, we provide the server, and the user only needs to download the app from the App Store and start using it. The server uses NAT to support multiple users, so each user does not need to set up a server to use the app.

Performance

Performance Histogram

This figure shows a histogram of 30 trials of transmitting 1400 bytes to our server directly across the Transmission layer and receiving those bytes back (in reversed order). It’s been normalized to 1KB, and we can see that the average time to transmit 1KB of data to the server and back is approximately 0.4 seconds. This is slow, but a huge improvement from our results when we first got data transmission working (80 seconds) and within the reasonable range of throughputs we expected to achieve.

Discussion

We had a lot of difficulty finding a suitably powerful API to perform DNS lookups on iOS. Of the APIs that were available, none were written in Objective-C or Swift, so we had to interoperate with low-level C code. Luckily, C functions can be called from Swift, so we were able to interact with these APIs without writing additional code. There were still challenges: namely, C headers cannot be exposed to Swift without first organizing them into a module-map, and though Swift is a high-level language with automatic reference counting and lambdas, we were unable to use these features when working with C code - we had to allocate context structs to store data about run-loop callbacks (rather than using lambdas) and we had to manually manage this memory.

Though we initially used a thread-based concurrency model on the client, we switched to a run-loop implementation to eliminate race-conditions and make the codebase easier to reason about. Instead of jumping between threads in the middle of execution, a run loop involves scheduling tasks to be completed in sequence, over-and-over, for the duration of the program. For example, we’re able to check for new data on a socket during the run-loop and call the appropriate handler.

We built a client library that implemented the Transmission layer protocol for sending arbitrary data to the server. The library originally used callbacks to asynchronously handle responses, allowing us to write non-blocking code even if running on a single thread. We later improved the library to use futures, an abstraction of callbacks. A future represents a value that doesn’t yet exist, but allows you to specify actions to take once it does exist. Further, it allows combinators to be built, such as a future that is only resolved once multiple other futures are resolved.

Our app uses try-catch exception handling to report errors that happen in lower layers of our framework to upper layers. This allows us to handle common error conditions and recovery, for example retrying when a lookup times out. Since the API is asynchronous, we had to design a mechanism to propagate errors through callbacks. We did this by wrapping all callback inputs in a Result type that might contain either the desired result or an error value. This abstraction allowed us to easily propagate errors regardless of where they occur. We even built a mechanism to pass server errors back to the client through this same mechanism. This greatly simplified the debugging process since we were more easily able to determine what went wrong when certain client lookups acted unexpectedly.

As we added more modules to our client app, it became more difficult to understand the logs. Furthermore, we often did not have verbose enough logs to locate an issue, and had to add more on a case-by-case basis. Eventually, we decided to build a logging class that allows us to easily specify the source module and severity of each log message and filter output by these criteria.

We discovered that iOS caps the memory usage of a network extension at only 6 MB and would kill our app if our memory usage exceeded that threshold. Therefore, we had to be careful to not spawn too many threads or attempt to forward too many packets simultaneously, else we’d run out of memory. We addressed this issue by limiting the number of packets being transmitted at once - once we exceed our fixed limit, we stop reading packets from the device hardware until enough of our packets have been received by the server.

Caching was both an issue, and a solution to an issue we had. Since DNS is cached for long periods of time, we had to make sure that the client would never send a DNS lookup that is identical to a previous one. Otherwise, the response would be returned from the cache instead of the server. Caching solved a problem we had with intermediate servers sending a DNS lookup more than once. This caused issues where the server would report an error in response to the second lookup when trying to end a Transmission, since the first lookup had already ended it. However, only the second lookup’s failed response would get sent back to the client. Thus, we decided to cache these responses so that we could return the first lookup’s successful response to the second lookup.

After switching to the DNS Service Discovery API, we soon discovered that some server responses were missing TXT records. This API does not expose the number of records in an answer, so it is not possible to write code that waits until they have all been received. We worked around this issue by always sending an additional TXT record from the server specifying the record count.

We originally planned to transmit our DNS traffic over TCP, and rely on TCP for error correction, retransmission, and in-order delivery. However, we realized that even if our client sent a lookup over TCP to its local DNS server, that intermediate server was likely to perform its recursive lookups over UDP, and there was no way to force it to use TCP. With this constraint in mind, we redesigned our algorithm to not rely on in-order delivery (by adding indices to Transmission continue calls) and to detect dropped lookups (by adding a length to Transmission end calls).

We encountered several concurrency issues on the server side. The first was logging - to avoid logs from different threads being uselessly interleaved, we developed a simple thread-safe logging API to print information to both the console and a file in the order it’s received. The second was ScaPy’s blocking send/receive function, which required us to create a new thread for each packet we send out. The documentation and mailing lists seemed to suggest that there would be resource sharing issues with the network hardware, but we were surprised to encounter no such issues. We did, however, have a consistent problem with send/receive threads never returning (if they never received a response packet) and so had to construct an intermediate timeout thread for each send/receive thread that terminates it after a certain maximum amount of time.

Conclusions

Our final product successfully intercepts packets on the client and communicates them over the DNS protocol to our server, which spoofs and forwards those packets across the Internet, receives response packets, and returns those packets to the client. We successfully implemented all components of our original plan for this project and they individually work correctly, but unfortunately the integrated product is too slow to successfully forward normal device network traffic. We hypothesize that the transport-layer protocols forwarded by our network-layer tunnel are unable to cope with the multiple-second delay introduced by the DNS protocol, which is not designed to be used in the way we used it. Even disregarding any transport-layer issues, the throughput was sufficiently low that only text-based webpages and perhaps messaging apps would be realistically usable over our tunnel.

In conclusion, we’re confident that we aren’t far off from a fully working prototype and we’re proud to have created a strong technical foundation for our app with clear and efficient layers of abstraction.

References

[1] http://www.daemon.be/maarten/dnstunnel.html
[2] https://www.vpnoverdns.com/faq.html
[3] http://archives.neohapsis.com/archives/bugtraq/1998_2/0079.html
[4] https://developer.apple.com/library/prerelease/ios/documentation/NetworkExtension/Reference/NETunnelProviderClassRef/index.html
[5] https://developer.apple.com/videos/play/wwdc2015/717/
[6] https://github.com/iCepa/iCepa
[7] http://www.smartinsights.com/mobile-marketing/mobile-marketing-analytics/mobile-marketing-statistics/
[8] http://blog.cloudmark.com/2014/10/07/dns-tunneling-abuses/

Appendices

Server Modules

Server Modules

Module Name: Transmission
Function: Implements the Transmission class from the Transmission layer. It holds all the data from a single Transmission, defined by a transmissionID. Its functions are called by the resolver as it receives pieces of a Transmission to add data to the Transmission, and to put together and return the completed Transmission. Transmission messages come in 3 types. A lookup to .begin.burrow.tech causes the server to create a new Transmission and return a Transmission ID to the client. A lookup to <transmission_ID>..continue.burrow.tech contains data that is added to the Transmission. A lookup to <# of pieces>.<transmission_ID>.end.burrow.tech means that the Transmission data is put together, and handled as part of a Session. The Session returns a reply that is sent to the client.
Authorship: Alex
Language: Python
Workload: ~60 lines
Possible Failure Conditions: If it receives a Transmission that is not one of the Transmission types, it will report an error. If it receives a .end call before receiving all of the .continue calls for that Transmission, it will raise an error.

Module Name: Burrow Resolver
Function: Implements the DNS resolver of the server. It handles DNS requests from a client on the Transmission layer. It is used to receive DNS requests to the server. It determines what type of transmission the DNS request is, and handles it accordingly, sending back a success or failure message for each piece of a Transmission it receives. Once it receives an end Transmission, the then sends the complete Transmission to the Session layer to get a reply. It then constructs any responses as a TXT record and sends it back to the client. It provides caching of responses in case intermediate DNS servers send a lookup to the server multiple times.
Authorship: Alex, Jaden
Language: Python
Workload: ~300 lines
Possible Failure Conditions: If it receives a .continue or .end call with a Transmission ID that does not exist or was ended too long ago, it will return an error to the client.

Module Name: Session
Function: Implements the packet forwarding component of a Session. A Session itself is a packet forwarding session that sits on top of the Transmission layer. The client sends the server a number of packets and queries the server for response packets in a Session. This class uses ScaPy to spoof and forward packets. Each session sends and receives packets on a unique port. It sends a number of packets and holds the responses until the client asks for packets from the Session.
Authorship: Alex, Mimi
Language: Python
Workload: ~60 lines
Possible Failure Conditions: It only forwards TCP and UDP IP packets. Any other packets will result in an error. If it runs out of ports to forward packets on, it will also return an error, but this should never happen unless sessions are not being ended properly.

Module Name: Serialized Message Handler
Function: This handles messages from the client by delimiting the message string using dashes and getting the first item in the message, which is the type. It calls the appropriate handler based on the message type. A Begin message causes it to return a Session ID and create a new instance of a Session. A Forward message contains packets that are forwarded using its Session. A Request message queries the Session for response packets to the forwarded packets. An End message closes the Session and frees up the port.
Authorship: Mimi, Alex
Language: Python
Workload: ~70 lines
Possible Failure Conditions: If it receives a Session that is not one of the Session types, it will report an error. Otherwise, if it receives an end session to a session ID that does not exist, it will return an error to the client. It will also pass errors from Session on to the client.

Module Name: Burrow Logging
Function: This module allows our server-side code to quickly and easily log to both stdout and a logfile from multiple files and multiple processes/threads simultaneously. It’s fully thread-safe, taking advantage of Python’s multiprocessing library to queue log events. It also allows us to easily define a LOG() function in each file that sets the default indentation level of logs from that file, making the output easier to parse across different layers of abstraction.
Authorship: Alex
Language: Python
Workload: ~30 lines
Possible Failure Conditions: N/A

Module Name: Server run script and configuration
Function: We obtained a domain name and configured it to use our own nameserver (with some difficulty, most registrars seem to assume their customers will take advantage of the free DNS service they provide). Since code had to be running on the actual nameserver for our domain in order to be tested, only one person could work on the server code at a time. To ensure more than one person wouldn’t try to modify the code on the server at the same time, we configured the server to only allow one SSH login at a time. And to enable quick iteration, we developed a simple bash script wrapper for the server that restarts it any time files are changed.
Authorship: Alex
Language: Bash
Workload: ~30 lines
Possible Failure Conditions: N/A

Module Name: dnslib
Function: **** Used for all DNS-related functions, including listening for, parsing, and replying to DNS requests.
Authorship:** ** Paul Chakravarti
Language: Python
Workload: N/A
Possible Failure Conditions: N/A

Module Name: ScaPy
Function: Used to spoof the source IP address and port in packets received from a client, to send the spoofed packets out on the network, receive the replies, and then un-spoof the replies so that their destination is the client.
Authorship: SecDev (Philippe Biondi)
Language: Python
Workload: N/A
Possible Failure Conditions: N/A

Client Modules

Client Modules

Module Name: App
Function: The app UI features a switch that is used to activate and deactivate the DNS tunnel. When activated, system-wide traffic is intercepted by our tunneling extension and sent through the server. The app code also configures the extension and uses basic animations to make the tunnel state clear.
Authorship: Jaden
Language: Swift
Workload: ~150 lines
Possible Failure Conditions: Reports an error if it is unable to configure the tunnel provider. For example, if the user does not give Burrow VPN permissions, the app will be unable to configure the tunnel provider.

Module Name: Burrow Tunnel Provider
Function: Subclass of NETunnelProvider that utilizes the Session Controller module to set up and tear down a tunneling session in response to system events. While the extension is running, the tunnel provider will read packets from the device and forward them over the tunnel using the session controller with each run loop iteration. Further, the tunnel provider will implement the session controller delegate method to handle received packets and inject them into the device.
Authorship: Jaden
Language: Swift
Workload: ~150 lines
Possible Failure Conditions: Receives errors from the session controller and propagates them upwards.

Module Name: Session Controller
Function: Handles the logic of communicating with the session layer on the server. Specifically, serializes messages and deserializes response from the server, sends messages over the transmission layer, and polls the server for packets ready to be sent back to the client, notifying the extension via a delegate method. Also handles tearing down server session once the client is finished tunneling.
Authorship: Jaden
Language: Swift
Workload: ~400 lines
Possible Failure Conditions:
Deserialization error: The sever returned a response with either invalid formatting or invalid contents.
Server error: The server was unable to fulfill a client request, and responded with an error message indicating unknown message type, unknown session id, or some undefined error.

Module Name: Transmission Manager
Function: This is the client’s complement to the server’s Transmission layer. It sends arbitrary data to the server by breaking the data up into domain name sized chunks, and encoding it in Base64. It will query .begin.burrow.tech to begin a Transmission and get a Transmission ID, then send the data over multiple queries to continue.burrow.tech using the Transmission ID and the index of the chunk of data being sent. After the whole Transmission has been sent, it will query <# of pieces>.<transmission_ID>.end.burrow.tech. It uses the DNS resolver to perform the queries.
Authorship: Jaden, Mimi
Language: Swift
Workload: ~230 lines
Possible Failure Conditions: Throws an error if it is unable to handle a given TXT record. Reasons include unexpected server response, server failure response, and transmission timeout. Also propagates DNSResolver errors upwards.

Module Name: DNS Resolver
Function: Provides a high level API for asynchronous DNS resolution using the run loop rather than threads. This prevents a whole class of concurrency bugs that might’ve existed in our previous threaded implementation. Built on top of the low level DNS Service Discovery C module, this API returns a Future that will be executed when the DNS lookup resolves successfully. The Transmission Manager uses this class to encode messages to the server as DNS lookups, receiving TXT record responses. This API also handles collecting multiple TXT record responses from the server and returning them together to the caller. Further, it exposes methods for parsing TXT records into attribute dictionaries.
Authorship: Jaden
Language: Swift
Workload: ~350 lines
Possible Failure Conditions: Throws an error if it cannot parse the TXT record. Also propagates DNS Service Discovery errors upwards.

Module Name: DNS Service Discovery
Function: DNS Service Discovery is an Apple API for discovering services over DNS. It is built as a C API, but we created a module-map to expose it to Swift. We ignore it’s service features and simply use it to resolve DNS lookups. Since it exposes the socket used by the lookup, we can wait until the socket is ready to read. This can be done asynchronously using a run loop event source. Note that we previously used the lib resolver API, but it blocked on DNS resolution, and spawning a new thread for each DNS lookup was bug-prone and inefficient.
Authorship: Apple (modularized by Jaden)
Language: C
Workload: N/A
Possible Failure Conditions: OS-level failure conditions such as out of memory or no network connectivity https://developer.apple.com/library/ios/documentation/Networking/Reference/DNSServiceDiscovery_CRef/#//apple_ref/doc/constant_group/DNS_Error_Codes

Module Name: Logger
Function: Provides the capability to easily log messages with differing severities and later filter messages by module and severity. Each module or file can create a log object specific to itself such that all logs generated by this code will be labeled with the category. Each log is either an error, a warning, an info message, a debug message, or a verbose message. We can set our desired severity for each module such that only useful logs will print to the console. This makes debugging much easier since we don’t need to sift through unrelated messages. Further, it allows use to easily obtain more info about what’s happening when we observe a problem in a specific module.
Authorship: Jaden
Language: Swift
Workload: ~100 lines
Possible Failure Conditions: N/A

Module Name: Future (Promises)
Function: Used instead of callbacks throughout the project in order to simplify the logic. Previously, it was easy to forget to call a callback on a certain code path, so we built this module to ensure correctness in our code. Further, it allows use to built combinators that operate on our callbacks. For example, we can build a callback that will only be called once all of its dependencies are called. This is useful in the Transmission Manager since we want to wait until all continue responses have been received before we send the end message.
Authorship: Jaden
Language: Swift
Workload: ~150 lines
Possible Failure Conditions: N/A

More Repositories

1

Helium

A floating browser window for OS X
Swift
3,586
star
2

Dimensional

Swift matrices with friendly semantics and a familiar interface
Swift
37
star
3

Fractional

Swift fractional number type for precise representations of rationals
Swift
30
star
4

Axiomatic

Swift unification framework for logic programming
Swift
29
star
5

Yield

Swift coroutines, or that one yield statement that Python has :P
Swift
25
star
6

Guac

Monadic do-notation in Python (requires pypy3)
Python
19
star
7

JGLayoutDotSyntax

Allows developers to set up autolayout constraints using familiar dot syntax—even with font sizes!
Objective-C
18
star
8

BidirectionalMap

Swift bidirectional mapping providing O(1) lookup between values
Swift
15
star
9

Mailbox

Swift Mailboxes as a shameless ripoff of Go's Channel feature
Swift
14
star
10

Implicits

Eliminate global state without the boilerplate!
Python
13
star
11

JGPixelData

JGPixelData simplifies the process of editing the raw pixel RGBA values of a UIImage
Objective-C
12
star
12

Calcula

Swift lambda calculus functions supporting intensional equality
Swift
12
star
13

CS-179-Project

Parallel Nash Equilibrium Computation via Genetic Algorithm
C++
9
star
14

Gluey

Swift bare-bones unification framework
Swift
9
star
15

Orderly

Sorted array types in Swift
Swift
9
star
16

Scanner

StringInterpolationConvertible-style scanf in Swift
HTML
9
star
17

Swiftlog

A simple Prolog-like language implemented entirely in Swift
Swift
8
star
18

Linky

Value-semantic linked list for Swift
Swift
7
star
19

Chemical

Swift chemical reaction simulator
Swift
6
star
20

Automata

A framework for easily building finite state machines in Swift
Swift
5
star
21

Spork

Swift generators that can be copied while maintaining independent state
Swift
5
star
22

JGBeacon

A group of classes making low energy bluetooth discovery and communciation between iOS devices nearly effortless
Objective-C
5
star
23

Racer

Eliminate Swift race conditions with Racer!
Swift
5
star
24

JGWebScreenshotter

A quick, convenient, and efficient way to download screenshots of websites
Objective-C
4
star
25

Sparse

Swift default-value dictionary for representing collections with many repeated values
Swift
4
star
26

Poetic

Esoteric programming language where the source code reads like poetry
TypeScript
4
star
27

Parsley

Swift recursive descendent parser combinator
Swift
4
star
28

similarity-topology

Efficient nearest neighbor search in Swift
Swift
4
star
29

Advent2016

Advent of Code 2016 Challenges, each completed in a different language!
Rust
4
star
30

core-lmdb

minimal yet powerful Swift interface to LMDB
Swift
3
star
31

Permute

Swift permuted collection type
Swift
3
star
32

GraphView

Swift view for representing dynamic view graphs
Swift
3
star
33

JGIrrationalNumber

Allows developer to define irrational numbers as infinite summations and iterate over the digits
Objective-C
3
star
34

JGReuseCache

A simple cache for reusing arbitrary objects to minimize unnecessary allocation
Objective-C
3
star
35

Edgy

Swift value-semantic graph modeling values connected by bidirectional edges
Swift
2
star
36

Typist

Type checker based on Axiomatic
Swift
2
star
37

Matchbox

Super simple parsing
Swift
2
star
38

swift-priority-heap

Swift data structure for efficiently accessing min/max elements by priority
Swift
2
star
39

NSAttributedStringDotSyntax

Objective-C
2
star
40

Erratic

Swift lazily shuffled collection for fast random index access
Swift
2
star
41

Stochastic

Stochastic discrete interaction simulator
Swift
2
star
42

JGWeakTarget

A proxy object used to weak reference the target in a target/selector callback.
Objective-C
2
star
43

JGCyclicFlasher

Simplifies the process of creating very short cyclic framed animations (with respect to the screen's refresh rate).
Objective-C
2
star
44

Handbag

Swift counted set for storing an unordered collection of non-unique elements
Swift
2
star
45

Tart

A simple dependently typed programming language based on…
Haskell
2
star
46

Comparator

Swift type for determining the ordering of two Comparable values
Swift
2
star
47

Caltech-Room-Combo-Generator

Generates combos that are compatible with Caltech locks
Python
1
star
48

FoldUI

Swift
1
star
49

JGMorseCodeKeyboard

Objective-C
1
star
50

Document

A Swift type for line break-delimited documents
Swift
1
star
51

JGBSSIDRanger

Objective-C
1
star
52

jadengeller.github.io

HTML
1
star
53

photo-utils

Swift
1
star
54

swift-lsm1

C
1
star
55

JGSmoothOverscrollCell

A UICollectionViewCell subclass with smooth swipe actions
Objective-C
1
star
56

Graham

An arbitrary base, arbitrary size integer type for Swift
Swift
1
star
57

Expressive

A lambda-supporting expression interpreter
Swift
1
star
58

JGFlippableView

Objective-C
1
star
59

Parsnip

Root of the Parsley parsing library
Swift
1
star
60

Generational

Swift generator package enabling concatenation, dropping, taking, iterating, cycling, and more!
Swift
1
star
61

CS-81-Project

Compiler Project
Swift
1
star