diff options
Diffstat (limited to 'source/http.c')
| -rw-r--r-- | source/http.c | 359 |
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 | |||
| 29 | static 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 | |||
| 93 | static 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 | |||
| 144 | int 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 | } | ||
