module 15.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * HTTP (c.module)
 *
Stewart Brodie's avatar
Stewart Brodie committed
18
 *  Acorn Computers Ltd. 1997, 1998
19 20 21 22 23 24 25 26
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kernel.h"
#include "swis.h"
27
#include "Global/ModHand.h"
28
#include "sys/errno.h"
Stewart Brodie's avatar
Stewart Brodie committed
29
#include "HTTPHdr.h"
30 31 32 33 34 35 36 37
#include "module.h"
#include "protocol.h"
#include "cookie.h"
#include "hosttrack.h"
#include "connpool.h"
#include "utils.h"
#include "dns.h"
#include "config.h"
38
#include "security.h"
39

40
static volatile int callback_pending_flag = 0;
41 42 43

#define NO_SUCH_SWI	(0x1E6)

Stewart Brodie's avatar
Stewart Brodie committed
44 45 46
#define URL_PROTOCOL_REGISTER	(0x83e20)
#define URL_PROTOCOL_DEREGISTER (0x83e21)

47 48 49 50
#define URL_00			(0x83e00)
#define URL_SSL_AVAILABLE	(0x83e02)


Stewart Brodie's avatar
Stewart Brodie committed
51
#ifndef ROM
ROOL's avatar
ROOL committed
52
extern int Resources(void); /* From resgen */
Stewart Brodie's avatar
Stewart Brodie committed
53 54 55
#endif

#ifndef ROM
56
#ifdef DEFINE_ERRNO
Stewart Brodie's avatar
Stewart Brodie committed
57 58
int __errno;
#endif
59
#endif
Stewart Brodie's avatar
Stewart Brodie committed
60

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

/*************************************************************/
/* So the real error number that we have to return is:	     */
/*************************************************************/
#define HTTP_ERROR_NUM  (ERROR_BASE+HTTP_OFFSET)

/*************************************************************/
/* The error message definitions			     */
/*************************************************************/
typedef enum {
	HTTP_HOST_NOT_FOUND	= HTTP_ERROR_NUM + 0,	/* DNS lookup failed */
	HTTP_HOST_CONNECT_ERROR = HTTP_ERROR_NUM + 1,	/* connect() failed */
	HTTP_DATA_READ_ERROR	= HTTP_ERROR_NUM + 2,	/* unexpected read error on socket */
	HTTP_GENERAL_ERROR	= HTTP_ERROR_NUM + 3,	/* Misc. - not helpful */
	HTTP_BAD_SESSION_ERROR	= HTTP_ERROR_NUM + 4,	/* Failed to locate session in session table */
	HTTP_CONNECTION_FAILED	= HTTP_ERROR_NUM + 5,	/* Failed to establish connection */
	HTTP_METHOD_UNSUPPORTED	= HTTP_ERROR_NUM + 6,	/* Unknown method in R2 */
	HTTP_METHOD_INIT_ERR	= HTTP_ERROR_NUM + 7,	/* Failed to start a fetch completely */
	HTTP_BAD_URL_PARSE	= HTTP_ERROR_NUM + 8,	/* Unable to parse URL (URL module too old?) */
80 81
	HTTP_PROXY_NOT_FOUND	= HTTP_ERROR_NUM + 9,	/* Unable to contact the proxy */
	HTTP_NO_SECURITY	= HTTP_ERROR_NUM + 10	/* No SSL support present */
82 83 84 85 86 87 88 89 90 91 92 93 94
} http_internal_error_codes;



/* Sets clibrary_realloc_routine_is_buggy non-zero if RISC OS 3.1 or earlier (ie. ROM realloc is broken) */
static int clibrary_realloc_routine_is_buggy;
static void module_check_os_version(void)
{
        int os;
        (void) _swix(OS_Byte, _INR(0,2)|_OUT(1), 129, 0, 255, &os);
        clibrary_realloc_routine_is_buggy = (os <= 0xA4);
}

95 96 97 98 99 100 101 102 103
static int registered_http;
static int registered_https;

static _kernel_oserror *try_to_register_for_https(void)
{
        if (!registered_https) {
                int ver = security_ssl_available();
                if (ver > 0) {
                        _kernel_oserror *e;
104 105 106 107 108 109 110 111 112 113 114 115
                        int base;
                        char info[sizeof("Acorn SSL  Acorn 1066 (Built: 14-Oct-1066)")];
                        const char *help, *bracket = NULL;

			e = _swix(OS_Module, _INR(0,1)|_OUT(3), ModHandReason_LookupName, "AcornSSL", &base);
			if (e == NULL) {
				help = (const char *)(base + *(int *)(base + Module_HelpStr));
				bracket = strstr(help, "(");
			}
			strcpy(info, "Acorn SSL  Acorn 1998");
			if (bracket) {
				strcat(info, " (Built: ");
116
				strncat(info, bracket + 1, sizeof("dd-Mmm-YYYY)"));
117
			}
118
			e = _swix(URL_PROTOCOL_REGISTER, _INR(0,4), 0, HTTP_SecureGetData, "https:",
119
				ver, info);
120 121 122 123 124 125 126 127
			if (e == NULL) {
		        	registered_https = 1;
			}
                }
        }

        return NULL;
}
128 129 130

static _kernel_oserror *try_to_register(void)
{
131
        _kernel_oserror *e = NULL;
132

133 134
	if (!registered_http) {
		e = _swix(URL_PROTOCOL_REGISTER, _INR(0,4), 0, HTTP_00, "http:",
Robert Sprowson's avatar
Robert Sprowson committed
135
			Module_VersionNumber, Module_Help "  Acorn 1997 (Built: " Module_Date ")");
136 137
		if (e != NULL) return e;
        	registered_http = 1;
138
	}
139 140 141 142 143

	if (registered_http) {
	        (void) try_to_register_for_https();
	}

144 145 146
        return e;
}

147 148 149 150 151 152 153 154 155 156 157
static _kernel_oserror *try_to_deregister_for_https(void)
{
        if (!registered_https) {
                return NULL;
        }
        else {
                registered_https = 0;
	        return _swix(URL_PROTOCOL_DEREGISTER, _INR(0,1), 0, HTTP_SecureGetData);
        }
}

158 159
static _kernel_oserror *try_to_deregister(void)
{
160 161
	(void) try_to_deregister_for_https();
        if (!registered_http) {
162 163 164
                return NULL;
        }
        else {
165
                registered_http = 0;
166 167 168 169 170 171 172 173 174 175 176
		return _swix(URL_PROTOCOL_DEREGISTER, _INR(0,1), 0, HTTP_00);
        }
}

static _kernel_oserror *register_with_url(void)
{
        _kernel_oserror *e;

	e = try_to_register();
	if (e == NULL) return e;
	if (e->errnum != NO_SUCH_SWI) return e;
Stewart Brodie's avatar
Stewart Brodie committed
177
	#ifndef ROM
178 179
	e = _swix(OS_Module, _INR(0,1), 1 /* Load */, "System:Modules.Network.URL.URL");
	return try_to_register();
Stewart Brodie's avatar
Stewart Brodie committed
180 181 182
	#else
	return NULL;
	#endif
183 184 185 186 187 188 189
}

/*************************************************************/
/* _kernel_oserror *module_init(char *cmd_fail, int podu...  */
/*************************************************************/
/* Start up and register ourselves with the URL module	     */
/*************************************************************/
Robert Sprowson's avatar
Robert Sprowson committed
190
_kernel_oserror *module_init(const char *cmd_tail, int podule_base, void *pw)
191 192 193 194 195 196
{
	_kernel_oserror *e;

	(void) podule_base;
	(void) cmd_tail;

197 198
	registered_http = 0;
	registered_https = 0;
199
	module_check_os_version();
200
	security_init();
201 202 203 204 205 206 207 208
	config_init();
	session_init();
	hosttrack_init();
	connpool_init();
	dns_init();

	e = register_with_url();
	if (e == NULL) {
ROOL's avatar
ROOL committed
209 210 211
#ifndef ROM
		e = _swix(ResourceFS_RegisterFiles, _IN(0), Resources());
#endif
212
		if (e == NULL) {
Stewart Brodie's avatar
Stewart Brodie committed
213 214 215 216
			if (getenv(Module_Title "$Path")) {
				e = messages_file_open(Module_Title ":Messages");
			}
			else {
Robert Sprowson's avatar
Robert Sprowson committed
217
				e = messages_file_open("Resources:$.Resources.URL." Module_Title ".Messages");
Stewart Brodie's avatar
Stewart Brodie committed
218
			}
219 220 221 222 223
			if (e == NULL) {
				callback_pending_flag = 0;
				e = _swix(OS_CallEvery, _INR(0,2), 5*100, callevery_entry, pw); /* 5 secs */
				if (e != NULL) messages_file_close();
			}
ROOL's avatar
ROOL committed
224 225 226
#ifndef ROM
			if (e != NULL) (void) _swix(ResourceFS_DeregisterFiles, _IN(0), Resources());
#endif
227 228 229 230
		}
		if (e != NULL) (void) try_to_deregister();
	}
	else {
Stewart Brodie's avatar
Stewart Brodie committed
231
		#ifndef ROM
232 233 234 235 236 237 238 239
  	        if (e->errnum == NO_SUCH_SWI) {
  	                _kernel_oserror *newe = find_error();
  	                const size_t length = strlen(strcpy(newe->errmess, e->errmess));

  	                newe->errnum = NO_SUCH_SWI;
			strncat(newe->errmess, " (Load the URL module)", sizeof(newe->errmess) - length);
			e = newe;
  	        }
Stewart Brodie's avatar
Stewart Brodie committed
240
		#endif
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
	}

	if (e == NULL) {
		read_cookie_file();
	}

	return e;
}

void service_handler(int service_number, _kernel_swi_regs *r, void *pw)
{
        #ifdef TRACE
        protocol_debug("Service &%X: R0 = %d for URL version  %03d\n", service_number, r->r[0], r->r[2]);
        #endif
        (void) pw;
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284

	if (service_number == URL_SSL_AVAILABLE) {
	        switch (r->r[0]) {
	                case 0:
	                        if (security_ssl_arrived(r->r[2])) {
	                                (void) try_to_register();
	                        }
		                break;
	                case 1:
	                        (void) security_ssl_gone();
	                	if (registered_https) {
                                        try_to_deregister_for_https();
                                }
	                	break;
                        default:
                                break;
	        }
	}
	else {
		switch (r->r[0]) {
		        case 0:
	        		(void) try_to_register();
	        		break;
		        case 1:
		        	(void) try_to_deregister();
	        		break;
		        default:
		        	break;
		}
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
	}
}

/*************************************************************/
/* _kernel_oserror *module_kill(int fatal, int podule, ...   */
/*************************************************************/
/* When we are killed off, deregister ourselves from the URL */
/* module						     */
/*************************************************************/
_kernel_oserror *module_kill(int fatal, int podule, void *pw)
{
	(void) podule;
	(void) fatal;

	_swix(OS_RemoveTickerEvent, _INR(0,1), callevery_entry, pw);
	if (callback_pending_flag) {
		_swix(OS_RemoveCallBack, _INR(0,1), callback_entry, pw);
	}

	ses_kill_all();
	session_init();
	hosttrack_exit();
	connpool_exit();
	http_free_agent();

	messages_file_close();
ROOL's avatar
ROOL committed
311 312 313
#ifndef ROM
	_swix(ResourceFS_DeregisterFiles, _IN(0), Resources());
#endif
314 315

	(void) try_to_deregister();
Stewart Brodie's avatar
Stewart Brodie committed
316

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
	return NULL;
}


/*************************************************************/
/* _kernel_oserror *swi_handler(int swi_no, _kernel_swi_...  */
/*************************************************************/
/* Upon a SWI call into this module decide what to do and    */
/* how to do it and all that. Just an excuse for a case	     */
/* statement						     */
/*************************************************************/
static _kernel_oserror *swi_handler_2(int swi_offset, _kernel_swi_regs *r, void *pw)
{
	/* Instead of adding the SWI chunk to the offset and comparing with the
	 * various cmhg defined macros, mask off the SWI chunk identifier at
	 * compile time.  This may well enable the C compiler to do better when
	 * it builds the switch table
	 */
	(void) pw;

	switch (swi_offset) {
		/* Generic protocol module SWIs */
		case HTTP_GetData - HTTP_00:
			return(http_start(r));

		case HTTP_Status - HTTP_00:
			return(http_status(r));

		case HTTP_ReadData - HTTP_00:
			return(http_readdata(r));

		case HTTP_Stop - HTTP_00:
			return(http_stop(r));

351 352
		/* Generic security protocol module SWIs */
		case HTTP_SecureGetData - HTTP_00:
353 354 355 356 357 358
		        if (security_ssl_available()) {
				return(https_start(r));
		        }
		        else {
		                return(make_error(HTTP_NO_SECURITY, 0));
		        }
359 360

		case HTTP_SecureStatus - HTTP_00:
361 362 363 364 365 366
		        if (security_ssl_available()) {
				return(https_status(r));
		        }
		        else {
		                return(make_error(HTTP_NO_SECURITY, 0));
		        }
367 368

		case HTTP_SecureReadData - HTTP_00:
369 370 371 372 373 374
		        if (security_ssl_available()) {
				return(https_readdata(r));
		        }
		        else {
		                return(make_error(HTTP_NO_SECURITY, 0));
		        }
375 376

		case HTTP_SecureStop - HTTP_00:
377 378 379 380 381 382
		        if (security_ssl_available()) {
				return(https_stop(r));
		        }
		        else {
		                return(make_error(HTTP_NO_SECURITY, 0));
		        }
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

		/* HTTP specific SWIs (at the upper end of the SWI range */
		#ifdef COOKIE
		case HTTP_EnumerateCookies - HTTP_00:
			return(enumerate_cookies(r));

		case HTTP_ConsumeCookie - HTTP_00:
			return(consume_cookie(r));

		case HTTP_AddCookie - HTTP_00:
			return(add_cookie(r));
		#endif

		default:
			return(error_BAD_SWI);
	}
}

_kernel_oserror *swi_handler(int swi_offset, _kernel_swi_regs *r, void *pw)
{
	_kernel_oserror *e;
	#ifdef TRACE
	static const char *swi[] = {"GetData", "Status", "ReadData", "Stop"};
406 407
	protocol_debug(">>Handling SWI &%02X (HTTP_%s%s) for R1=%08x\n", swi_offset,
		(swi_offset >= 16 && (swi_offset-16) < (sizeof(swi)/sizeof(char*))) ? "Secure" : "",
408
		swi_offset < (sizeof(swi)/sizeof(char*)) ? swi[swi_offset] :
409
		(swi_offset >= 16 && (swi_offset-16) < (sizeof(swi)/sizeof(char*))) ? swi[swi_offset-16] :
Stewart Brodie's avatar
Stewart Brodie committed
410 411 412
		swi_offset == (HTTP_EnumerateCookies - HTTP_00) ? "EnumerateCookies" :
		swi_offset == (HTTP_ConsumeCookie - HTTP_00) ? "ConsumeCookie" :
		swi_offset == (HTTP_AddCookie - HTTP_00) ? "AddCookie" : "UNKNOWN!",
413 414 415 416
		r->r[1]);
	#endif
	e = swi_handler_2(swi_offset, r, pw);
	#ifdef TRACE
417
	protocol_debug("<<Exiting  SWI &%02X (HTTP_%s%s) for R1=%08x  %s "
418
		"R0=%s; R2=%d (=&%p); R3=%d; R4=%d; R5=%d\n\n", swi_offset,
419
		(swi_offset >= 16 && (swi_offset-16) < (sizeof(swi)/sizeof(char*))) ? "Secure" : "",
420
		swi_offset < (sizeof(swi)/sizeof(char*)) ? swi[swi_offset] :
421
		(swi_offset >= 16 && (swi_offset-16) < (sizeof(swi)/sizeof(char*))) ? swi[swi_offset-16] :
Stewart Brodie's avatar
Stewart Brodie committed
422 423 424
		swi_offset == (HTTP_EnumerateCookies - HTTP_00) ? "EnumerateCookies" :
		swi_offset == (HTTP_ConsumeCookie - HTTP_00 ) ? "ConsumeCookie" :
		swi_offset == (HTTP_AddCookie - HTTP_00 ) ? "AddCookie" : "UNKNOWN!",
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
		r->r[1],
		e ? e->errmess : "<V flag clear>",
		e ? "<errorblock>" : protocol_network_states(r->r[0]),
		r->r[2], (void *) r->r[2], r->r[3], r->r[4], r->r[5]);
	#endif

	return e;
}

/*************************************************************/
/* _kernel_oserror *return_error(int status_code)	     */
/*************************************************************/
/* Given a HTTP internal status code generate the correct    */
/* error block.						     */
/*************************************************************/
_kernel_oserror *return_error(int status_code)
{
	#ifdef TRACE
	protocol_debug("http:return_error called (code %d)\n",status_code);
	#endif

	if (status_code < HTTP_CONNECT_TIMED_OUT) {
	        #ifdef TRACE
	        protocol_debug("!!>> BAD STATUS CODE IN RETURN_ERROR\n");
	        #endif
	        return NULL;
	}
	switch (status_code) {
		case HTTP_ERROR_NOLINK:
			/* This is not *really* an error (fatal)... */
			/* StB: so what's it doing here then?? */
			return NULL;

		case HTTP_CONNECT_TIMED_OUT:
			return make_error(HTTP_HOST_CONNECT_ERROR,0);

		case HTTP_ERROR_READ:
			return make_error(HTTP_DATA_READ_ERROR,0);

		case HTTP_ERROR_HOSTNAME:
			return make_error(HTTP_HOST_NOT_FOUND,0);

		case HTTP_ERROR_BADSESSION:
			return make_error(HTTP_BAD_SESSION_ERROR,0);

		case HTTP_ERROR_NOCONNECTION:
			return make_error(HTTP_CONNECTION_FAILED,0);

		case HTTP_ERROR_BAD_METHOD:
			return make_error(HTTP_METHOD_UNSUPPORTED,0);

		case HTTP_ERROR_INIT_ERR:
			return make_error(HTTP_METHOD_INIT_ERR,0);

		case HTTP_ERROR_NO_PARSE:
			return make_error(HTTP_BAD_URL_PARSE,0);

		case HTTP_ERROR_NO_PROXY:
			return make_error(HTTP_PROXY_NOT_FOUND,0);

485 486 487
		case HTTP_ERROR_NO_SECURITY:
		        return make_error(HTTP_NO_SECURITY,0);

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
		default:
			return make_error(HTTP_GENERAL_ERROR,0);
	}
}


/* Poll the active connection pool */
int callback_handler(_kernel_swi_regs *r, void *pw)
{
	(void) pw;
	(void) r;

	if (callback_pending_flag == 0) {
		return 1;
	}

	connpool_poll();
	session_check();

	callback_pending_flag = 0;
	return 1;
}

int callevery_handler(_kernel_swi_regs *r, void *pw)
{
	(void) r;

	if (callback_pending_flag==1) {
		return 1;
	}

	callback_pending_flag = 1;
	_swix(OS_AddCallBack, _INR(0,1), callback_entry, pw);
	return 1;
}


/* RISC OS 3.1 SVC mode realloc is broken, so we must provide our own */
char *module_realloc(void *ptr, size_t size)
{
        #ifdef TRACE
        protocol_debug("module_realloc(%p, %d)\n", ptr, size);
        #endif
	if (!clibrary_realloc_routine_is_buggy) return realloc(ptr, size);

        if (ptr == NULL) {
                return malloc(size);
        }
        if (size == 0) {
                free(ptr);
		return NULL;
        }
	else {
	        const int *rma_block = ptr;
	        const size_t newsize = size - (rma_block[-1] - 4);
	        char *newptr;

		if (_swix(OS_Module, _IN(0)|_INR(2,3)|_OUT(2), 0xD, ptr, newsize, &newptr) != NULL) {
		        return NULL;
		}

		return newptr;
	}
}