Add HTTP timeout, use while loop for redirects, use constant for user agent.

This commit is contained in:
Steveice10 2017-03-19 13:45:56 -07:00
parent dbe6b992e5
commit c319a07f29
4 changed files with 70 additions and 31 deletions

View File

@ -670,7 +670,10 @@ u16* util_select_bnr_title(BNR* bnr) {
return bnr->titles[systemLanguage]; return bnr->titles[systemLanguage];
} }
static char util_http_redirect_buffer[1024]; #define HTTP_TIMEOUT 30000000000
#define MAKE_HTTP_USER_AGENT(major, minor, micro) ("Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/" #major "." #minor "." #micro)
#define HTTP_USER_AGENT MAKE_HTTP_USER_AGENT(VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO)
Result util_http_open(httpcContext* context, u32* responseCode, const char* url, bool userAgent) { Result util_http_open(httpcContext* context, u32* responseCode, const char* url, bool userAgent) {
return util_http_open_ranged(context, responseCode, url, userAgent, 0, 0); return util_http_open_ranged(context, responseCode, url, userAgent, 0, 0);
@ -681,11 +684,8 @@ Result util_http_open_ranged(httpcContext* context, u32* responseCode, const cha
return R_FBI_INVALID_ARGUMENT; return R_FBI_INVALID_ARGUMENT;
} }
Result res = 0; char currUrl[1024];
strncpy(currUrl, url, sizeof(currUrl));
if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, url, 1))) {
char agent[128];
snprintf(agent, sizeof(agent), "Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
char range[64]; char range[64];
if(rangeEnd > rangeStart) { if(rangeEnd > rangeStart) {
@ -694,16 +694,30 @@ Result util_http_open_ranged(httpcContext* context, u32* responseCode, const cha
snprintf(range, sizeof(range), "%lu-", rangeStart); snprintf(range, sizeof(range), "%lu-", rangeStart);
} }
u32 response = 0; Result res = 0;
if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify)) && (!userAgent || R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "User-Agent", agent))) && (rangeStart == 0 || R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "Range", range))) && R_SUCCEEDED(res = httpcSetKeepAlive(context, HTTPC_KEEPALIVE_ENABLED)) && R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &response))) {
if(response == 301 || response == 302 || response == 303) {
memset(util_http_redirect_buffer, '\0', sizeof(util_http_redirect_buffer));
if(R_SUCCEEDED(res = httpcGetResponseHeader(context, "Location", util_http_redirect_buffer, sizeof(util_http_redirect_buffer)))) {
httpcCloseContext(context);
return util_http_open_ranged(context, responseCode, util_http_redirect_buffer, userAgent, rangeStart, rangeEnd); bool resolved = false;
u32 redirectCount = 0;
while(R_SUCCEEDED(res) && !resolved && redirectCount < 32) {
if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, currUrl, 1))) {
u32 response = 0;
if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify))
&& (!userAgent || R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "User-Agent", HTTP_USER_AGENT)))
&& (rangeStart == 0 || R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "Range", range)))
&& R_SUCCEEDED(res = httpcAddRequestHeaderField(context, "Accept-Encoding", "gzip"))
&& R_SUCCEEDED(res = httpcSetKeepAlive(context, HTTPC_KEEPALIVE_ENABLED))
&& R_SUCCEEDED(res = httpcBeginRequest(context))
&& R_SUCCEEDED(res = httpcGetResponseStatusCodeTimeout(context, &response, HTTP_TIMEOUT))) {
if(response == 301 || response == 302 || response == 303) {
redirectCount++;
memset(currUrl, '\0', sizeof(currUrl));
if(R_SUCCEEDED(res = httpcGetResponseHeader(context, "Location", currUrl, sizeof(currUrl)))) {
httpcCloseContext(context);
} }
} else { } else {
resolved = true;
if(responseCode != NULL) { if(responseCode != NULL) {
*responseCode = response; *responseCode = response;
} }
@ -718,6 +732,11 @@ Result util_http_open_ranged(httpcContext* context, u32* responseCode, const cha
httpcCloseContext(context); httpcCloseContext(context);
} }
} }
}
if(R_SUCCEEDED(res) && redirectCount >= 32) {
res = R_FBI_TOO_MANY_REDIRECTS;
}
return res; return res;
} }
@ -735,8 +754,25 @@ Result util_http_read(httpcContext* context, u32* bytesRead, void* buffer, u32 s
return R_FBI_INVALID_ARGUMENT; return R_FBI_INVALID_ARGUMENT;
} }
Result res = httpcDownloadData(context, buffer, size, bytesRead); Result res = 0;
return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0;
u32 startPos = 0;
if(R_SUCCEEDED(res = httpcGetDownloadSizeState(context, &startPos, NULL))) {
u32 outPos = 0;
while(outPos < size && R_SUCCEEDED(res)) {
u32 currPos = 0;
if((R_SUCCEEDED(res = httpcReceiveDataTimeout(context, &((u8*) buffer)[outPos], size - outPos, HTTP_TIMEOUT)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING)
&& R_SUCCEEDED(res = httpcGetDownloadSizeState(context, &currPos, NULL))) {
outPos = currPos - startPos;
}
}
if(R_SUCCEEDED(res) && bytesRead != NULL) {
*bytesRead = outPos;
}
}
return res;
} }
Result util_http_close(httpcContext* context) { Result util_http_close(httpcContext* context) {

View File

@ -500,6 +500,8 @@ static const char* description_to_string(Result res) {
return "Parse failed"; return "Parse failed";
case R_FBI_BAD_DATA: case R_FBI_BAD_DATA:
return "Bad data"; return "Bad data";
case R_FBI_TOO_MANY_REDIRECTS:
return "Too many redirects";
default: default:
break; break;
} }

View File

@ -7,6 +7,7 @@
#define R_FBI_THREAD_CREATE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 5) #define R_FBI_THREAD_CREATE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 5)
#define R_FBI_PARSE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 6) #define R_FBI_PARSE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 6)
#define R_FBI_BAD_DATA MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 7) #define R_FBI_BAD_DATA MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 7)
#define R_FBI_TOO_MANY_REDIRECTS MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 8)
#define R_FBI_NOT_IMPLEMENTED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, RD_NOT_IMPLEMENTED) #define R_FBI_NOT_IMPLEMENTED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, RD_NOT_IMPLEMENTED)
#define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY) #define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY)

View File

@ -150,7 +150,7 @@ static void remoteinstall_network_update(ui_view* view, void* data, float* progr
} }
size = ntohl(size); size = ntohl(size);
if(size >= 128 * 1024) { if(size >= INSTALL_URL_MAX * INSTALL_URLS_MAX) {
error_display(NULL, NULL, "Payload too large."); error_display(NULL, NULL, "Payload too large.");
remoteinstall_network_close_client(data); remoteinstall_network_close_client(data);