Skip to content
This repository was archived by the owner on Apr 16, 2020. It is now read-only.

Commit 4687dbe

Browse files
committed
Deprecate NEKit
1 parent 2d525c2 commit 4687dbe

File tree

1 file changed

+18
-250
lines changed

1 file changed

+18
-250
lines changed

README.md

+18-250
Original file line numberDiff line numberDiff line change
@@ -1,272 +1,40 @@
11
# NEKit
22

3-
[![Join the chat at https://gitter.im/zhuhaow/NEKit](https://badges.gitter.im/zhuhaow/NEKit.svg)](https://gitter.im/zhuhaow/NEKit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://telegram.me/NEKitGroup](https://img.shields.io/badge/chat-on%20Telegram-blue.svg)](https://telegram.me/NEKitGroup) [![Build Status](https://travis-ci.org/zhuhaow/NEKit.svg?branch=master)](https://travis-ci.org/zhuhaow/NEKit) [![GitHub release](https://img.shields.io/github/release/zhuhaow/NEKit.svg)](https://github.com/zhuhaow/NEKit/releases) [![codecov](https://codecov.io/gh/zhuhaow/NEKit/branch/master/graph/badge.svg)](https://codecov.io/gh/zhuhaow/NEKit) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![GitHub license](https://img.shields.io/badge/license-BSD_3--Clause-blue.svg)](LICENSE.md)
3+
NEKit is deprecated.
44

5-
**Take a look at the next generation of this project, [libnekit](https://github.com/zhuhaow/libnekit).**
5+
It should still work but I'm not intent on maintaining it anymore. It has many
6+
flaws and needs a revamp to be a high-quality library.
67

7-
A toolkit for Network Extension Framework.
8+
The architecture of NEKit is not ideal due to my lack of experience when I was
9+
writing it. To make it worse, it's not in its most efficient form as there
10+
wasn't a good async io library while people abuse it for simple features that
11+
should be just implemented directly or use other lightweight libraries.
812

9-
NEKit is the successor of [Soca](https://github.com/zhuhaow/soca-ios). The main goal of NEKit is to provide things needed in building a Network Extension app with `NETunnelProvider` to bypass network filtering and censorship while keep the framework as non-opinionated as possible.
13+
NEKit is not, was never intended, and probably will never become a library that
14+
simply works correctly out-of-the-box without understanding what it is doing
15+
under the hook. Through all these years, one thing I learned is for some function
16+
this low level, the developer should understand what oneself is doing. I'm
17+
always concerned that people are creating apps that slowing down users' phones
18+
unnecessarily because of this library and I feel responsible.
1019

11-
**NEKit does not depend on Network Extension framework. You can use NEKit without Network Extension entitlement to build a rule based proxy in a few lines.**
12-
13-
**Do not enable `TUNInterface` as of now since it is not working.**
14-
15-
There are two demos that you should check out.
16-
17-
[SpechtLite](https://github.com/zhuhaow/SpechtLite) does not require Network Extension and anyone can use it.
18-
19-
[Specht](https://github.com/zhuhaow/Specht) is another demo that requires Network Extension entitlement.
20-
21-
Currently, NEKit supports:
22-
23-
- Forward requests through different proxies based on remote host location, remote host domain or the connection speed of proxies.
24-
- Integrated tun2socks framework to reassemble TCP packets into TCP flows.
25-
- A DNS server that rewrites request and response.
26-
- Some tools to build IP packets.
27-
- ...
28-
29-
**Part of the following content may be out of date. But the general design philosophy still holds.**
30-
Check document [here](https://zhuhaow.github.io/NEKit), which should be up to date.
31-
32-
Also, you may be more interested in [Potatso](https://github.com/shadowsocks/Potatso-iOS) if you just need an open source iOS app with GUI supporting shadowsocks.
33-
34-
**[Wingy](https://itunes.apple.com/cn/app/wingy-mian-fei-banvpn-ke-hu/id1148026741?mt=8) is a free app built with NEKit available on App Store for your iDevice.** Note Wingy is not created by or affiliated with me.
35-
36-
If you have any questions (not bug report), **please join Gitter or Telegram instead of opening an issue**.
37-
38-
## Principle
39-
40-
NEKit tries to be as flexible and non-opionated as possible.
41-
42-
However, it is not always as modular as you may think if you want to reproduce transport layer from network layer.
43-
44-
NEKit follows one fundamental principle to keep the best network performance: The device connecting to target server directly resolves the domain.
45-
46-
This should not be a problem if the applications on your device connect to the local proxy server directly, where we can get the request domain information then send that to remote proxy server if needed.
47-
48-
But consider that if an application tries to make a socket connection by itself, which generally consists of two steps:
49-
50-
1. Make a DNS lookup to find the IP address of the target server.
51-
2. Connect to the remote server by socket API provided by the system.
52-
53-
We can read only two independent things from the TUN interface, a UDP packet containing the DNS lookup request and a TCP flow consisting of a serial of TCP packets. So there is no way we can know the initial request domain for the TCP flow. And since there may be multiple domains served on the same host, we can not get the origin domain by saving the DNS response and looking that up reversely later.
54-
55-
The only solution is to create a fake IP pool and assign each requested domain with a unique fake IP so we can look that up reversely. Every connection needs to look that up from the DNS server afterwards; this is the only non-modular part of NEKit which is already encapsulated in `ConnectSession`.
56-
57-
## Usage
58-
59-
### Add it to you project
60-
I recommend adding this project to your project, which is easier to debug.
61-
62-
However, you can still use it with Carthage (you'll need Carthage anyway since NEKit uses Carthage) by adding
63-
```ruby
64-
github "zhuhaow/NEKit"
65-
```
66-
to you `Cartfile`.
67-
68-
Use
69-
```carthage update --no-use-binaries --platform mac,ios```
70-
to install all frameworks. Do not use pre-compiled binaries since some of them might be buggy.
71-
72-
### Overview
73-
NEKit basically consists of two parts, a proxy server forwarding socket data based on user defined rules and an IP stack reassembling IP packets back to TCP flow as a socket.
74-
75-
### Rule manager
76-
Before starting any proxy server, we need to define rules.
77-
78-
Each rule consists of two parts, one defining what kinding of request matches this rule and the other defining what adapter to use. An adapter represents the abstraction of a socket connection to a remote proxy server (or remote host). We use `AdapterFactory` to build adapters.
79-
80-
NEKit provides `AdapterSocket` supporting HTTP/HTTPS/SOCK5 proxy and Shadowsocks(AES-128-CFB/AES-192-CFB/AES-256-CFB/chacha20/salsa20/rc4-md5). Let me know if there is any other type of proxy needed. You can also implement your own `AdapterSocket`.
81-
82-
```swift
83-
// Define remote adapter first
84-
let directAdapterFactory = DirectAdapterFactory()
85-
let httpAdapterFactory = HTTPAdapterFactory(serverHost: "remote.http.proxy", serverPort: 3128, auth: nil)
86-
let ssAdapterFactory = ShadowsocksAdapterFactory(serverHost: "remote.ss.proxy", serverPort: 7077, encryptMethod: "AES-256-CFB", password: "1234567890")
87-
88-
// Then create rules
89-
// To use geo ip database, first init it
90-
// Download the binary database https://dev.maxmind.com/geoip/geoip2/geolite2/
91-
GeoIP.database = MMDB("PATH_TO_THE_DATABASE")!
92-
let chinaRule = CountryRule(countryCode: "CN", match: true, adapterFactory: directAdapterFactory)
93-
// `urls` are regular expressions
94-
let listRule = try! ListRule(adapterFactory: ssAdapterFactory, urls: ["some\\.site\\.does\\.not\\.exists"])
95-
let allRule = AllRule(adapterFactory: httpAdapterFactory)
96-
97-
// Create rule manager, rules will be matched in order.
98-
let manager = RuleManager(fromRules: [listRule, chinaRule, allRule], appendDirect: true)
99-
100-
// Set this manager as the active manager
101-
RuleManager.currentManager = ruleManager
102-
```
103-
104-
There is also `Configuration` to load rules from a Yaml config file. But that is not recommended.
105-
106-
107-
### Proxy server
108-
Now we can start a HTTP/SOCKS5 proxy server locally.
109-
110-
```swift
111-
let server = GCDHTTPProxyServer(address: IPv4Address(fromString: "127.0.0.1"), port: Port(port: 9090))
112-
try! server.start()
113-
```
114-
115-
Now there is a HTTP proxy server running on `127.0.0.1:9090` which will forward requests based on rules defined in `RuleManager.currentManager`.
116-
117-
If you do not want to handle IP packets, then that's it, just set the proxy to `127.0.0.1:9090` in System Preferences and you are good to go.
118-
119-
If you want to read on, you will have to request Network Extention entitlement from Apple.
120-
121-
But even if you use NetworkExtention to set up the network proxy, it does not mean you have to process packets, just do not route anything to the TUN interface and do not set up `IPStack`. For iOS, if you claim you have implemented it but just do nothing the users probably will never notice.
122-
123-
### IP stack
124-
125-
Assuming you already set up a working extension with correct routing configurations (Google how, this is not trivial).
126-
127-
In
128-
129-
```swift
130-
startTunnelWithOptions(options: [String : NSObject]?, completionHandler: (NSError?) -> Void)
131-
```
132-
set `RuleManager` and start proxy server(s) and then create an instance representing the TUN interface by
133-
134-
```swift
135-
let stack = TUNInterface(packetFlow: packetFlow)
136-
```
137-
138-
We also have to set
139-
140-
```swift
141-
RawSocketFactory.TunnelProvider = self
142-
```
143-
to create socket to connect to remote servers with `NETunnelProvider`.
144-
145-
Then we need to register ip stacks implementing `IPStackProtocol` to process IP packets.
146-
147-
NEKit provides several stacks.
148-
149-
#### TCPStack
150-
`TCPStack` process TCP packets and reassembles them back into TCP flows then send them to the proxy server specified by `proxyServer` variable. You have to set `proxyServer` before registering `TCPStack` to `TUNInterface`.
151-
152-
#### DNSServer
153-
DNS server is implemented as an IP stack processing UDP packets send to it.
154-
155-
First create an DNS server with a fake IP pool. (You should use fake IP, but you can disable it if you want to by set it to nil.)
156-
157-
```swift
158-
let fakeIPPool = IPv4Pool(start: IPv4Address(fromString: "172.169.1.0"), end: IPv4Address(fromString: "172.169.255.0"))
159-
let dnsServer = DNSServer(address: IPv4Address(fromString: "172.169.0.1"), port: Port(port: 53), fakeIPPool: fakeIPPool)
160-
```
161-
162-
Then we have to define how to resolve the DNS requests, NEKit provides the most trivial one which sends the request to remote DNS server directly with UDP protocol, you can do anything you want by implementing `DNSResolverProtocol`.
163-
164-
```swift
165-
let resolver = UDPDNSResolver(address: IPv4Address(fromString: "114.114.114.114"), port: Port(port: 53))
166-
dnsServer.registerResolver(resolver)
167-
```
168-
169-
It is very important to set
170-
171-
```swift
172-
DNSServer.currentServer = dnsServer
173-
```
174-
so we can look up the fake IP reversely.
175-
176-
#### UDPDirectStack
177-
`UDPDirectStack` sends and reads UDP packets to and from remote server directly.
178-
179-
180-
You can register these stack to TUN interface by
181-
182-
```swift
183-
interface.registerStack(dnsServer)
184-
// Note this sends out every UDP packets directly so this must comes after any other stack that processes UDP packets.
185-
interface.registerStack(UDPDirectStack())
186-
interface.registerStack(TCPStack.stack)
187-
```
188-
189-
When everything is set up, you should start processing packets by calling `interface.start()` in the completion handler of `setTunnelNetworkSettings`.
190-
191-
## Event
192-
193-
You can use `Observer<T>` to observe the events in proxy servers and sockets. Check out observers in `DebugObserver.swift` as an example.
194-
195-
## Dive in
196-
197-
### Framework overview
198-
The structure of the proxy server is given as follows:
199-
200-
```
201-
┌──────────────────────────────────────────────────┐
202-
│ │
203-
│ ProxyServer │
204-
│ │
205-
├──────┬───┬──────┬───┬──────┬───┬──────┬───┬──────┤
206-
│Tunnel│ │Tunnel│ │Tunnel│ │Tunnel│ │Tunnel│
207-
└──────┘ └──────┘ └───▲──┘ └──────┘ └──────┘
208-
╱ ╲
209-
╱───────╱ ╲─────╲
210-
╱ ╲
211-
┌────────▼────────┐ ┌────────▼────────┐
212-
│ ProxySocket │ │ AdapterSocket │
213-
└────────▲────────┘ └────────▲────────┘
214-
│ │
215-
│ │
216-
┌────────▼────────┐ ┌────────▼────────┐
217-
│RawSocketProtocol│ │RawSocketProtocol│
218-
└────────▲────────┘ └────────▲────────┘
219-
│ │
220-
│ │
221-
╔══════▼═══════╗ ╔═══════▼══════╗
222-
║ LOCAL ║ ║ REMOTE ║
223-
╚══════════════╝ ╚══════════════╝
224-
```
225-
226-
When a new socket is accepted from the listening socket of the proxy server, it is wrapped in some implementation of `RawSocketProtocol` as a raw socket which just reads and writes data.
227-
228-
Then it is wrapped in a subclass of `ProxySocket` which encapsulates the proxy logic.
229-
230-
The `TCPStack` wraps the reassembled TCP flow (`TUNTCPSocket`) in `DirectProxySocket` then send it to the `mainProxy` server.
231-
232-
Similarly, `AdapterSocket` encapsulated the logic of how to connect to remote and process the data flow.
233-
234-
Pretty much everything of NEKit follows the [delegation pattern](https://en.wikipedia.org/wiki/Delegation_pattern). If you are not familiar with that, you should learn it first, probabaly by learning how to use [CocoaAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket) (note that the sockets in NEKit are **not** thread-safe which is different from GCDAsyncSocket).
235-
236-
### The lifetime of a `Tunnel`
237-
238-
When a `RawSocketProtocol` socket is accepted or created by `TCPStack`, it is wrapped in a `ProxySocket` then in a `Tunnel`. The `Tunnel` will call `proxySocket.openSocket()` to let the proxy socket start process data.
239-
240-
When the `ProxySocket` read enough data to build a `ConnectSession`, it calls `func didReceiveRequest(request: ConnectSession, from: ProxySocket)` of the `delegate` (which should be the `Tunnel`).
241-
242-
The `Tunnel` then matches this request in `RuleManager` to get the corresponding `AdapterFactory`. Then `func openSocketWithSession(session: ConnectSession)` of the produced `AdapterSocket` is called to connect to remote server.
243-
244-
The `AdapterSocket` calls `func didConnect(adapterSocket: AdapterSocket, withResponse response: ConnectResponse)` of the `Tunnel` to let the `ProxySocket` has a chance to respond to remote response. (This is ignored as of now.)
245-
246-
Finally, when the `ProxySocket` and `AdapterSocket` are ready to forward data, they should call `func readyToForward(socket: SocketProtocol)` of the `Tunnel` to let it know. When both sides are ready, the tunnel will read from both sides and then send the received data to the other side intact.
247-
248-
When any side of the tunnel is disconnected, the `func didDisconnect(socket: SocketProtocol)` is called then both sides are closed actively. The `Tunnel` will be released when both sides disconnect successfully.
249-
250-
251-
## TODO
252-
- [ ] Documents.
253-
- [ ] IPv6 support.
20+
Thanks for everyone who has contributed, used or interested in this library.
25421

25522
## License
23+
25624
Copyright (c) 2016, Zhuhao Wang
25725
All rights reserved.
25826

25927
Redistribution and use in source and binary forms, with or without
26028
modification, are permitted provided that the following conditions are met:
26129

262-
* Redistributions of source code must retain the above copyright notice, this
30+
- Redistributions of source code must retain the above copyright notice, this
26331
list of conditions and the following disclaimer.
26432

265-
* Redistributions in binary form must reproduce the above copyright notice,
33+
- Redistributions in binary form must reproduce the above copyright notice,
26634
this list of conditions and the following disclaimer in the documentation
26735
and/or other materials provided with the distribution.
26836

269-
* Neither the name of NEKit nor the names of its
37+
- Neither the name of NEKit nor the names of its
27038
contributors may be used to endorse or promote products derived from
27139
this software without specific prior written permission.
27240

0 commit comments

Comments
 (0)