forked from sparkleDai/swiftp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxy_protocol.txt
More file actions
232 lines (199 loc) · 11.1 KB
/
proxy_protocol.txt
File metadata and controls
232 lines (199 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
This document describes the protocol for communication between the SwiFTP server app
and the cloud proxy app, which is running on some server out on the net.
BASIC DESIGN
------------
This whole proxy system exists in order to work around limitations on inbound connections.
For example, my cellular service provider, T-Mobile, blocks inbound TCP connections.
The workaround is to create a proxy service out on the internet.
The Android device is not able to accept inbound connections, but it is free to create
outbound connections. The Android device therefore establishes and permanently maintains
a TCP connection to the proxy server. We call this the "command connection." This
connection exists as long as SwiFTP is running, unless the user explicitly disables it.
When an FTP client wishes to connect via the proxy, it initiates a normal FTP
connection to the proxy. The proxy informs the SwiFTP Android app that there is an
incoming
FTP session waiting to be established. The SwiFTP Android app then establishes another
TCP connection to the proxy (in addition to the existing control connection), which
will be used
exclusively to send and receive data from the FTP client.
An important bit of vocabulary:
- The "command" connection is the TCP connection from the SwiFTP Android app to the
proxy server. This connection uses our own brand-new protocol which is documented
below.
- The "control" connection is the normal FTP connection between the FTP client and the
SwiFTP Android app. This uses the normal FTP protocol, where the client sends things
like USER, LIST, RETR and the server responds with "200 OK", "503 Forbidden", etc.
DATA SOCKETS
------------
The design of the FTP protocol dictates that new TCP connections, separate from the
control connection, be created every time there is a directory listing or file transfer.
When it is time to perform an action that requires a new data socket (a new TCP connection
with the client, the SwiFTP Android app creates another TCP connection to the proxy. The
exact details depend on whether the connection is in PORT or PASV mode (see the API
section).
DISAMBIGUATING DEVICES AND SESSIONS
-----------------------------------
Since the proxy server is running on a single IP and port that serves multiple devices
and users, it needs some way of matching up incoming connections. For instance, in
PASV mode file transfer, both the SwiFTP Android app and the FTP client make an inbound
connection to the proxy server. How does the proxy know to match up these two
connections out of the many others that are being created at the same time? The answer
is that we use something called a "prefix."
A prefix is a six-character alphanumeric code that uniquely identifies a device running
the SwiFTP Android app. It is assigned by the proxy server upon creation of
a control connection and given to the SwiFTP Android app.
AUTHENTICATION
--------------
Secure identification of devices is accomplished by assigning a unique "secret" to each
device running the SwiFTP Android app. This secret is assigned the first time that the
device connects to the proxy, and is persisted on the device. This is not terribly
secure but is considered good enough.
This system is not secure against spoofed values of ANDROID_ID. This is unfortunate
but probably good enough.
API
---
Requests and responses are all in JSON. All requests have a top-level string named
"action" that gives the request type. For instance, the request to create an account
would look something like the following:
{
"action" : "create_account"
"android_id : "1234567890"
}
A response is a JSON object that is given in response to a request. As a convention,
if an error occurred while processing a request, the response object will contain
top-level strings named "error_code" and "error_string" identifying the error. The
error_code is a number uniquely identifying the error, and error_string is a human-
readable description of the error.
For example, the following object might be returned as a response to the above
create_account request if the account could not be created:
{
"error_code" : 10
"error_string": "An account already exists for that ANDROID_ID"
}
Or, if the operation was successful,
{
"secret" : "abcdefghijklmnopqrstuvwxyz0123456789"
}
Successful requests may result in a response that is the empty object, if there is no
error to report and
the request did not require any values to be returned. This would be the empty JSON
object, "{}". For instance, a request with action "noop" would prompt an empty response.
After the TCP connection has been created from the SwiFTP Android app to the proxy
server, the SwiFTP Android app must send one of:
- create_account
- authenticate
List of requests from SwiFTP Android app to proxy server:
Action: create_account (DEPRECATED)
Purpose: Called only once upon first run of the SwiFTP Android app. Causes an account
to be created and a secret to be created. The returned secret should be
stored in persistent memory. If a device sends this request and receives
a response indicating success, then that session is authenticated, and the
device can send other requests.
Params: "android_id": the unique device identifier provided by the Android API
"swiftp_version": the version of SwiFTP server installed
Returns: "secret": the secret "password" that this device will use to authenticate
Errors: 12: the ANDROID_ID failed sanity check
Action: authenticate (DEPRECATED)
Purpose: Authenticate the device. Called before any other command (except
create_account).
Params: "android_id": the unique device identifier provided by the Android API
"secret": this device's secret
"swiftp_version": the version of SwiFTP server installed
Returns: Empty object on success
Errors: 11: The ANDROID_ID isn't in the database, or the secret is wrong
12: There has been a database error, and account info was lost. Delete
stored secret and create a new account.
Action: start_command_session
Purpose: Authenticate device and begin a command session
Params: None
Returns: "prefix": the prefix string identifying this session and device (see above)
Action: data_pasv_listen
Purpose: Cause proxy to start listening on a new port for
an incoming TCP connection from an FTP client. This will be called by the
SwiFTP Android app after it has received a PASV command from the FTP client,
so the proxy can begin listening for an inbound data connection from the
FTP client.
Params: None
Returns: "port": a number giving the port that the server is listening on.
Errors: 15: The device has overrun its quota
Action: data_pasv_accept
Purpose: After a pasv_listen action, the proxy will be listening for an
inbound connection from an FTP client. This request will cause the proxy
to do an accept() for incoming conections from the FTP client. A pasv_accept
must be sent over the same connection as the associated pasv_listen request.
On success, after the empty JSON object is returned, the socket will switch
into proxying mode, where it will simply pass data back and forth between
the FTP client and the SwiFTP Android app (it will no longer accept requests).
Params: None
Returns: Empty object on success
Errors: 13: Proxy waited too long without receiving an incoming connection
Action: data_port_connect
Purpose: Open an outbound connection to the given address and port and begin data
transfer. If this action is successful, the TCP connection will no longer
accept JSON requests and will become a simple bidirectional proxy between
the FTP client and the SwiFTP Android app.
Params: "address": the address to which the proxy should open a connection
"port": the port to which the proxy should open a connection
Returns: Empty object on success
Errors: 14: The proxy couldn't open a connection to the given address and port.
15: The device has overrun its quota
Action: check_quota
Purpose: Retrieve the number of bytes used by the device, as well as the maximum number
of bytes that the device may use. The user should be prompted to increase
their quota if the quota is near exhaustion.
Params: None
Returns: "quota": the max number of bytes that the device may use
"used": the number of bytes used so far
Errors: None specific to this request
Action: finished
Purpose: Send by the client to log out. It is not required to be sent, but it might be
useful someday to distinguish between normal exits and dropped connections.
Params: None
Returns: Empty object on success
Errors: None specific to this request
Requests from proxy server to SwiFTP app:
Action: control_connection_waiting
Purpose: This is sent over the command connection to instruct the SwiFTP Android app to
open a new socket to the given location and use it as a new FTP connection.
This means there is an FTP client that has connected and is waiting to be
serviced. The SwiFTP Android app should open a new connection to the proxy,
authenticate, then send accept_control_connection.
Params: "port": the port to which the SwiFTP Android app should connect
Errors: None specific to this request
Action: reconnect
Purpose: The proxy might send this message before it goes down for maintenance. The
SwiFTP Android app should disconnect soon and restart the connection process,
which will probably result in a connection to a different proxy server. The
Android app should do this in a way that doesn't interrupt the user in the
middle of a transfer, if possible.
Params: Optional: "newserver": the hostname of a new server that is recommended for
the client to connect to next.
Errors: None specific to this message
Action: prefer_server
Purpose: The Android device should try to connect to the given server on its next
attempt, instead of picking a random server. If the connection to that
server fails, then the Android device should do the normal random server
selection.
Params: "host": the DNS name of the preferred server
Errors: None specific to this message
Requests that may be sent by either party:
Action: noop
Purpose: Does nothing, could be used as a connection keepalive
Params: none
Returns: empty response
Action: message
Purpose: Pass a human-readable message. If received by the Android device, the
message should be displayed to the user in a dialog box. This could
be used for service alerts.
Params: text: a string containing the message
Returns: empty response
Errors: None specific to this request
LIST OF ERROR CODES
-------------------
0: Internal server error. The errorString value may contain more information.
9: The request was not correctly formatted, or was missing arguments.
10: An account could not be created because that ANDROID_ID already has an account
11: Either the ANDROID_ID or secret is not valid
12: Invalid ANDROID_ID value
13: PASV socket accept timeout in pasv_accept
14: Outbound socket connection error in port_connect