about summary refs log tree commit diff stats
path: root/source/http.c
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-09-16 16:11:15 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-09-16 16:11:15 -0400
commit4fe95cc1e7412e309d026e53cd44239bd3ef031d (patch)
treec1b711ed5a81976d9054da6205f5f9b2c3133248 /source/http.c
parente2a76d1f0fd978f285edf1dbc0f6e87cf89c63ce (diff)
downloadgen3uploader-4fe95cc1e7412e309d026e53cd44239bd3ef031d.tar.gz
gen3uploader-4fe95cc1e7412e309d026e53cd44239bd3ef031d.tar.bz2
gen3uploader-4fe95cc1e7412e309d026e53cd44239bd3ef031d.zip
Wii can now send a POST request to a website
The extractor now uses libfat to read a config file off the root of the
SD card, a model for which is included in the repository. The extractor
is capable of negotiating an HTTPS connection, but it requires a
download of the target site's root CA certificate to be on the SD card
and pointed at by the config file. TLS/SSL functionality is provided by
wolfSSL. I had to make a couple of minor changes to wolfSSL for it to
work properly, and those changes are located in the devkitpro branch of
my fork, hatkirby/wolfssl.

The Wii program now arranges some of the information that the GBA sends
it into a JSON object, which is then sent off (along with some
authentication information from the config file) to the endpoint defined
in the config file. The code used to maintain the network connection is
from the WiiTweet project, which was licensed under the GPL. Some of the
code used to send the HTTP request also comes from said project.
Diffstat (limited to 'source/http.c')
-rw-r--r--source/http.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/source/http.c b/source/http.c new file mode 100644 index 0000000..1979e22 --- /dev/null +++ b/source/http.c
@@ -0,0 +1,359 @@
1/*
2 * Copyright (C) 2008 Tantric
3 * Copyright (C) 2012 Pedro Aguiar
4 * Copyright (C) 2017 hatkirby
5 *
6 * Originally part of the WiiTweet project, which was distributed under the
7 * GPL.
8 */
9#include "http.h"
10#include <gccore.h>
11#include <network.h>
12#include <unistd.h>
13#include <fcntl.h>
14#include <ogcsys.h>
15#include <ogc/lwp_watchdog.h>
16#include <sys/errno.h>
17#include <string.h>
18#include <stdio.h>
19
20#define TCP_CONNECT_TIMEOUT 4000
21#define TCP_SEND_SIZE (32 * 1024)
22#define TCP_RECV_SIZE (32 * 1024)
23#define TCP_BLOCK_RECV_TIMEOUT 4000
24#define TCP_BLOCK_SEND_TIMEOUT 4000
25#define TCP_BLOCK_SIZE 1024
26#define HTTP_TIMEOUT 10000
27#define IOS_O_NONBLOCK 0x04
28
29static int tcpWrite(
30 bool secure,
31 void* firstParam,
32 const u8* buffer,
33 u32 length)
34{
35 int left = length;
36 int sent = 0;
37 int step = 0;
38
39 u64 t = gettime();
40 while (left)
41 {
42 if (ticks_to_millisecs(diff_ticks(t, gettime())) > TCP_BLOCK_SEND_TIMEOUT)
43 {
44 return HTTPR_ERR_TIMEOUT;
45 }
46
47 int block = left;
48 if (block > TCP_SEND_SIZE)
49 {
50 block = TCP_SEND_SIZE;
51 }
52
53 int result;
54 if (secure)
55 {
56 result = wolfSSL_write(*(WOLFSSL**)firstParam, buffer, block);
57 } else {
58 result = net_write(*(s32*)firstParam, buffer, block);
59 }
60
61 if ((result == 0) || (result == -56))
62 {
63 usleep(20 * 1000);
64
65 continue;
66 }
67
68 if (result < 0)
69 {
70 break;
71 }
72
73 sent += result;
74 left -= result;
75 buffer += result;
76 usleep(100);
77
78 if ((sent / TCP_BLOCK_SIZE) > step)
79 {
80 t = gettime();
81 step++;
82 }
83 }
84
85 if (left == 0)
86 {
87 return HTTPR_OK;
88 } else {
89 return HTTPR_ERR_WRITE;
90 }
91}
92
93static int tcpRead(
94 bool secure,
95 void* firstParam,
96 char* buffer,
97 u16 maxLength)
98{
99 u64 startTime = gettime();
100 u16 cur = 0;
101
102 while (cur < maxLength)
103 {
104 if (ticks_to_millisecs(diff_ticks(startTime, gettime())) > HTTP_TIMEOUT)
105 {
106 return HTTPR_ERR_TIMEOUT;
107 }
108
109 u32 result;
110 if (secure)
111 {
112 result = wolfSSL_read(*(WOLFSSL**)firstParam, &buffer[cur], 1);
113 } else {
114 result = net_read(*(s32*)firstParam, &buffer[cur], 1);
115 }
116
117 if (result == -EAGAIN)
118 {
119 usleep(20 * 1000);
120
121 continue;
122 }
123
124 if (result <= 0)
125 {
126 break;
127 }
128
129 if ((cur > 0) && (buffer[cur-1] == '\r') && (buffer[cur] == '\n'))
130 {
131 buffer[cur-1] = 0;
132
133 return HTTPR_OK;
134 }
135
136 cur++;
137 startTime = gettime();
138 usleep(100);
139 }
140
141 return HTTPR_ERR_RECEIVE;
142}
143
144int submitToApi(
145 const char* endpoint,
146 WOLFSSL_CTX* sslContext,
147 cJSON* data,
148 const char* username,
149 const char* password)
150{
151 // Form request body.
152 cJSON* jRequestBody = cJSON_CreateObject();
153
154 cJSON_AddItemToObject(jRequestBody, "game", data);
155
156 cJSON_AddItemToObject(
157 jRequestBody,
158 "username",
159 cJSON_CreateString(username));
160
161 cJSON_AddItemToObject(
162 jRequestBody,
163 "password",
164 cJSON_CreateString(password));
165
166 const char* requestBody = cJSON_PrintUnformatted(jRequestBody);
167
168 // Gather data from endpoint URL.
169 char httpHost[64];
170 char httpPath[256];
171 int httpPort = 80;
172 bool secure = false;
173
174 if (!strncasecmp(endpoint, "https://", 8))
175 {
176 endpoint += 8;
177 secure = true;
178 httpPort = 443;
179 } else if (!strncasecmp(endpoint, "http://", 7))
180 {
181 endpoint += 7;
182 } else {
183 return 10;
184 }
185
186 char* solidus = strchr(endpoint, '/');
187 if ((solidus == NULL) || (solidus[0] == 0))
188 {
189 return 11;
190 }
191
192 char* colon = strchr(endpoint, ':');
193 if (colon != NULL)
194 {
195 snprintf(httpHost, colon - endpoint + 1, "%s", endpoint);
196 sscanf(colon + 1, "%d", &httpPort);
197 } else {
198 snprintf(httpHost, solidus - endpoint + 1, "%s", endpoint);
199 }
200
201 strcpy(httpPath, solidus);
202
203 // Connect to endpoint.
204 printf("Connecting...\n");
205
206 s32 socket = net_socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
207 if (socket < 0)
208 {
209 return HTTPR_ERR_CONNECT;
210 }
211
212 if (!secure)
213 {
214 int result = net_fcntl(socket, F_GETFL, 0);
215 if (result < 0)
216 {
217 net_close(socket);
218
219 return HTTPR_ERR_CONNECT;
220 }
221
222 if (net_fcntl(socket, F_SETFL, result | IOS_O_NONBLOCK))
223 {
224 net_close(socket);
225
226 return HTTPR_ERR_CONNECT;
227 }
228 }
229
230 struct sockaddr_in sa;
231 memset(&sa, 0, sizeof(struct sockaddr_in));
232 sa.sin_family = PF_INET;
233 sa.sin_len = sizeof(struct sockaddr_in);
234 sa.sin_port = htons(httpPort);
235
236 struct in_addr inAddr;
237 if ((strlen(httpHost) < 16) && (inet_aton(httpHost, &inAddr)))
238 {
239 sa.sin_addr.s_addr = inAddr.s_addr;
240 } else {
241 struct hostent* hp = net_gethostbyname(httpHost);
242 if (!hp || (hp->h_addrtype != PF_INET))
243 {
244 return HTTPR_ERR_CONNECT;
245 }
246
247 memcpy(
248 (char*) &sa.sin_addr,
249 hp->h_addr_list[0],
250 hp->h_length);
251 }
252
253 u64 time1 = ticks_to_secs(gettime());
254 s32 connectResult;
255 do
256 {
257 connectResult = net_connect(
258 socket,
259 (struct sockaddr*) &sa,
260 sizeof(sa));
261
262 if (ticks_to_secs(gettime()) - time1 > TCP_CONNECT_TIMEOUT*1000)
263 {
264 break;
265 }
266 } while (connectResult != -EISCONN);
267
268 if (connectResult != -EISCONN)
269 {
270 net_close(socket);
271
272 return HTTPR_ERR_CONNECT;
273 }
274
275 // Initialize SSL, if necessary.
276 WOLFSSL* sslHandle = 0;
277 if (secure)
278 {
279 sslHandle = wolfSSL_new(sslContext);
280 if (sslHandle == NULL)
281 {
282 net_close(socket);
283
284 return HTTPR_ERR_SSL;
285 }
286
287 wolfSSL_set_fd(sslHandle, socket);
288 }
289
290 // Send request.
291 printf("Sending...\n");
292
293 void* firstParam = 0;
294
295 if (secure)
296 {
297 firstParam = &sslHandle;
298 } else {
299 firstParam = &socket;
300 }
301
302 char requestHeader[1024];
303 char* r = requestHeader;
304 r += sprintf(r, "POST %s HTTP/1.1\r\n", httpPath);
305 r += sprintf(r, "Host: %s\r\n", httpHost);
306 r += sprintf(r, "Content-Type: application/json\r\n");
307 r += sprintf(r, "Content-Length: %d\r\n\r\n", strlen(requestBody));
308
309 int result = tcpWrite(
310 secure,
311 firstParam,
312 (u8*) requestHeader,
313 strlen(requestHeader));
314 if (result != HTTPR_OK)
315 {
316 return result;
317 }
318
319 result = tcpWrite(
320 secure,
321 firstParam,
322 (u8*) requestBody,
323 strlen(requestBody));
324
325 if (result != HTTPR_OK)
326 {
327 return result;
328 }
329
330 // Read response.
331 char line[1024];
332 int httpStatus = 404;
333 while (!tcpRead(secure, firstParam, line, 1024))
334 {
335 if (!line[0])
336 {
337 break;
338 }
339
340 sscanf(line, "HTTP/1.%*u %u", &httpStatus);
341 }
342
343 if (httpStatus != 200)
344 {
345 return HTTPR_ERR_STATUS;
346 }
347
348 // Clean up.
349 if (secure)
350 {
351 wolfSSL_free(sslHandle);
352 }
353
354 net_close(socket);
355
356 cJSON_Delete(jRequestBody);
357
358 return HTTPR_OK;
359}