1
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
3
** Redistribution and use in source and binary forms, with or without
4
** modification, are permitted provided that the following conditions
6
** 1. Redistributions of source code must retain the above copyright
7
** notice, this list of conditions and the following disclaimer.
8
** 2. Redistributions in binary form must reproduce the above copyright
9
** notice, this list of conditions and the following disclaimer in the
10
** documentation and/or other materials provided with the distribution.
11
** 3. The name of the author may not be used to endorse or promote products
12
** derived from this software without specific prior written permission.
14
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
** There is more copyright information in the bottom half of this file.
27
** Please see it for more details. */
29
#include "xmlrpc_config.h"
38
#include "xmlrpc_server.h"
39
#include "xmlrpc_int.h"
40
#include "xmlrpc_server_abyss.h"
41
#include "xmlrpc_server_abyss_int.h"
44
/*=========================================================================
45
** die_if_fault_occurred
46
**=========================================================================
47
** If certain kinds of out-of-memory errors occur during server setup,
48
** we want to quit and print an error.
51
static void die_if_fault_occurred(xmlrpc_env *env) {
52
if (env->fault_occurred) {
53
fprintf(stderr, "Unexpected XML-RPC fault: %s (%d)\n",
54
env->fault_string, env->fault_code);
61
/*=========================================================================
63
**=========================================================================
64
** Blast some XML data back to the client.
68
send_xml_data (TSession * const r,
72
const char * const http_cookie = NULL;
73
/* This used to set http_cookie to getenv("HTTP_COOKIE"), but
74
that doesn't make any sense -- environment variables are not
75
appropriate for this. So for now, cookie code is disabled.
79
/* fwrite(buffer, sizeof(char), len, stderr); */
81
/* XXX - Is it safe to chunk our response? */
84
ResponseStatus(r, 200);
87
/* There's an auth cookie, so pass it back in the response. */
89
char *cookie_response;
91
cookie_response = malloc(10+strlen(http_cookie));
92
sprintf(cookie_response, "auth=%s", http_cookie);
94
/* Return abyss response. */
95
ResponseAddField(r, "Set-Cookie", cookie_response);
97
free(cookie_response);
101
ResponseContentType(r, "text/xml; charset=\"utf-8\"");
102
ResponseContentLength(r, len);
106
HTTPWrite(r, buffer, len);
112
/*=========================================================================
114
**=========================================================================
115
** Send an error back to the client.
119
send_error(TSession * const abyssSessionP,
120
unsigned int const status) {
122
ResponseStatus(abyssSessionP, (uint16) status);
123
ResponseError(abyssSessionP);
128
/*=========================================================================
130
**=========================================================================
131
** Extract some data from the TConn's underlying input buffer. Do not
132
** extract more than 'max'.
136
get_buffer_data(TSession * const r,
138
char ** const out_start,
139
int * const out_len) {
141
/* Point to the start of our data. */
142
*out_start = &r->conn->buffer[r->conn->bufferpos];
144
/* Decide how much data to retrieve. */
145
*out_len = r->conn->buffersize - r->conn->bufferpos;
149
/* Update our buffer position. */
150
r->conn->bufferpos += *out_len;
155
/*=========================================================================
157
**=========================================================================
158
** Slurp the body of the request into an xmlrpc_mem_block.
162
getBody(xmlrpc_env * const envP,
163
TSession * const abyssSessionP,
164
unsigned int const contentSize,
165
xmlrpc_mem_block ** const bodyP) {
166
/*----------------------------------------------------------------------------
167
Get the entire body from the Abyss session and return it as the new
170
The first chunk of the body may already be in Abyss's buffer. We
171
retrieve that before reading more.
172
-----------------------------------------------------------------------------*/
173
xmlrpc_mem_block * body;
175
body = xmlrpc_mem_block_new(envP, 0);
176
if (!envP->fault_occurred) {
177
unsigned int bytesRead;
183
while (!envP->fault_occurred && bytesRead < contentSize) {
184
get_buffer_data(abyssSessionP, contentSize - bytesRead,
185
&chunkPtr, &chunkLen);
186
bytesRead += chunkLen;
188
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body,
191
if (bytesRead < contentSize) {
192
/* Get the next chunk of data from the connection into the
195
abyss_bool succeeded;
197
/* Reset our read buffer & flush data from previous reads. */
198
ConnReadInit(abyssSessionP->conn);
200
/* Read more network data into our buffer. If we encounter
201
a timeout, exit immediately. We're very forgiving about
202
the timeout here. We allow a full timeout per network
203
read, which would allow somebody to keep a connection
204
alive nearly indefinitely. But it's hard to do anything
205
intelligent here without very complicated code.
207
succeeded = ConnRead(abyssSessionP->conn,
208
abyssSessionP->server->timeout);
210
xmlrpc_env_set_fault_formatted(
211
envP, XMLRPC_TIMEOUT_ERROR, "Timed out waiting for "
212
"client to send its POST data");
215
if (envP->fault_occurred)
216
xmlrpc_mem_block_free(body);
225
storeCookies(TSession * const httpRequestP,
226
unsigned int * const httpErrorP) {
227
/*----------------------------------------------------------------------------
228
Get the cookie settings from the HTTP headers and remember them for
230
-----------------------------------------------------------------------------*/
231
const char * const cookie = RequestHeaderValue(httpRequestP, "cookie");
234
Setting the value in an environment variable doesn't make
235
any sense. So for now, cookie code is disabled.
238
setenv("HTTP_COOKIE", cookie, 1);
241
/* TODO: parse HTTP_COOKIE to find auth pair, if there is one */
250
validateContentType(TSession * const httpRequestP,
251
unsigned int * const httpErrorP) {
252
/*----------------------------------------------------------------------------
253
If the client didn't specify a content-type of "text/xml", return
254
"400 Bad Request". We can't allow the client to default this header,
255
because some firewall software may rely on all XML-RPC requests
256
using the POST method and a content-type of "text/xml".
257
-----------------------------------------------------------------------------*/
258
const char * const content_type =
259
RequestHeaderValue(httpRequestP, "content-type");
260
if (content_type == NULL || strcmp(content_type, "text/xml") != 0)
269
processContentLength(TSession * const httpRequestP,
270
unsigned int * const inputLenP,
271
unsigned int * const httpErrorP) {
272
/*----------------------------------------------------------------------------
273
Make sure the content length is present and non-zero. This is
274
technically required by XML-RPC, but we only enforce it because we
275
don't want to figure out how to safely handle HTTP < 1.1 requests
276
without it. If the length is missing, return "411 Length Required".
277
-----------------------------------------------------------------------------*/
278
const char * const content_length =
279
RequestHeaderValue(httpRequestP, "content-length");
280
if (content_length == NULL)
283
int const contentLengthValue = atoi(content_length);
284
if (contentLengthValue <= 0)
288
*inputLenP = (unsigned int)contentLengthValue;
294
/****************************************************************************
295
Abyss handlers (to be registered with and called by Abyss)
296
****************************************************************************/
298
/* XXX - This variable is *not* currently threadsafe. Once the server has
299
** been started, it must be treated as read-only. */
300
static xmlrpc_registry *global_registryP;
302
static const char * trace_abyss;
305
processCall(TSession * const abyssSessionP,
306
int const inputLen) {
307
/*----------------------------------------------------------------------------
308
Handle an RPC request. This is an HTTP request that has the proper form
309
to be one of our RPCs.
310
-----------------------------------------------------------------------------*/
314
fprintf(stderr, "xmlrpc_server_abyss RPC2 handler processing RPC.\n");
316
xmlrpc_env_init(&env);
318
/* SECURITY: Make sure our content length is legal.
319
XXX - We can cast 'inputLen' because we know it's >= 0, yes?
321
if ((size_t) inputLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
322
xmlrpc_env_set_fault_formatted(
323
&env, XMLRPC_LIMIT_EXCEEDED_ERROR,
324
"XML-RPC request too large (%d bytes)", inputLen);
326
xmlrpc_mem_block *body;
327
/* Read XML data off the wire. */
328
getBody(&env, abyssSessionP, inputLen, &body);
329
if (!env.fault_occurred) {
330
xmlrpc_mem_block * output;
331
/* Process the RPC. */
332
output = xmlrpc_registry_process_call(
333
&env, global_registryP, NULL,
334
XMLRPC_MEMBLOCK_CONTENTS(char, body),
335
XMLRPC_MEMBLOCK_SIZE(char, body));
336
if (!env.fault_occurred) {
337
/* Send our the result. */
338
send_xml_data(abyssSessionP,
339
XMLRPC_MEMBLOCK_CONTENTS(char, output),
340
XMLRPC_MEMBLOCK_SIZE(char, output));
342
XMLRPC_MEMBLOCK_FREE(char, output);
344
XMLRPC_MEMBLOCK_FREE(char, body);
347
if (env.fault_occurred) {
348
if (env.fault_code == XMLRPC_TIMEOUT_ERROR)
349
send_error(abyssSessionP, 408); /* 408 Request Timeout */
351
send_error(abyssSessionP, 500); /* 500 Internal Server Error */
354
xmlrpc_env_clean(&env);
359
/*=========================================================================
360
** xmlrpc_server_abyss_rpc2_handler
361
**=========================================================================
362
** This handler processes all requests to '/RPC2'. See the header for
363
** more documentation.
367
xmlrpc_server_abyss_rpc2_handler (TSession * const r) {
372
fprintf(stderr, "xmlrpc_server_abyss RPC2 handler called.\n");
374
/* We handle only requests to /RPC2, the default XML-RPC URL.
375
Everything else we pass through to other handlers.
377
if (strcmp(r->uri, "/RPC2") != 0)
382
/* We understand only the POST HTTP method. For anything else, return
383
"405 Method Not Allowed".
385
if (r->method != m_post)
388
unsigned int httpError;
389
storeCookies(r, &httpError);
391
send_error(r, httpError);
393
unsigned int httpError;
394
validateContentType(r, &httpError);
396
send_error(r, httpError);
398
unsigned int httpError;
401
processContentLength(r, &inputLen, &httpError);
403
send_error(r, httpError);
405
processCall(r, inputLen);
411
fprintf(stderr, "xmlrpc_server_abyss RPC2 handler returning.\n");
417
/*=========================================================================
418
** xmlrpc_server_abyss_default_handler
419
**=========================================================================
420
** This handler returns a 404 Not Found for all requests. See the header
421
** for more documentation.
425
xmlrpc_server_abyss_default_handler (TSession * const r) {
433
/**************************************************************************
435
** The code below was adapted from the main.c file of the Abyss webserver
436
** project. In addition to the other copyrights on this file, the following
437
** code is also under this copyright:
439
** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
440
** All rights reserved.
442
** Redistribution and use in source and binary forms, with or without
443
** modification, are permitted provided that the following conditions
445
** 1. Redistributions of source code must retain the above copyright
446
** notice, this list of conditions and the following disclaimer.
447
** 2. Redistributions in binary form must reproduce the above copyright
448
** notice, this list of conditions and the following disclaimer in the
449
** documentation and/or other materials provided with the distribution.
450
** 3. The name of the author may not be used to endorse or promote products
451
** derived from this software without specific prior written permission.
453
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
454
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
455
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
456
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
457
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
458
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
459
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
460
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
461
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
462
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
465
**************************************************************************/
479
#include <sys/signal.h>
480
#include <sys/wait.h>
487
sigterm(int const sig) {
488
TraceExit("Signal %d received. Exiting...\n",sig);
495
sigchld(int const sig ATTR_UNUSED) {
496
/*----------------------------------------------------------------------------
497
This is a signal handler for a SIGCHLD signal (which informs us that
498
one of our child processes has terminated).
500
We respond by reaping the zombie process.
502
Implementation note: In some systems, just setting the signal handler
503
to SIG_IGN (ignore signal) does this. In others, it doesn't.
504
-----------------------------------------------------------------------------*/
508
/* Reap defunct children until there aren't any more. */
510
pid = waitpid( (pid_t) -1, &status, WNOHANG );
517
/* because of ptrace */
527
static TServer globalSrv;
528
/* When you use the old interface (xmlrpc_server_abyss_init(), etc.),
529
this is the Abyss server to which they refer. Obviously, there can be
530
only one Abyss server per program using this interface.
535
xmlrpc_server_abyss_init(int const flags ATTR_UNUSED,
536
const char * const config_file) {
541
ServerCreate(&globalSrv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
543
ConfReadServerFile(config_file, &globalSrv);
545
xmlrpc_server_abyss_init_registry();
546
/* Installs /RPC2 handler and default handler that use the
550
ServerInit(&globalSrv);
556
setupSignalHandlers(void) {
558
struct sigaction mysigaction;
560
sigemptyset(&mysigaction.sa_mask);
561
mysigaction.sa_flags = 0;
563
/* These signals abort the program, with tracing */
564
mysigaction.sa_handler = sigterm;
565
sigaction(SIGTERM, &mysigaction, NULL);
566
sigaction(SIGINT, &mysigaction, NULL);
567
sigaction(SIGHUP, &mysigaction, NULL);
568
sigaction(SIGUSR1, &mysigaction, NULL);
570
/* This signal indicates connection closed in the middle */
571
mysigaction.sa_handler = SIG_IGN;
572
sigaction(SIGPIPE, &mysigaction, NULL);
574
/* This signal indicates a child process (request handler) has died */
575
mysigaction.sa_handler = sigchld;
576
sigaction(SIGCHLD, &mysigaction, NULL);
583
runServer(TServer * const srvP,
584
runfirstFn const runfirst,
585
void * const runfirstArg) {
587
setupSignalHandlers();
590
/* Become a daemon */
595
TraceExit("Unable to become a daemon");
602
/* Change the current user if we are root */
604
if (srvP->uid == (uid_t)-1)
605
TraceExit("Can't run under root privileges. "
606
"Please add a User option in your "
607
"Abyss configuration file.");
609
#ifdef HAVE_SETGROUPS
610
if (setgroups(0,NULL)==(-1))
611
TraceExit("Failed to setup the group.");
612
if (srvP->gid != (gid_t)-1)
613
if (setgid(srvP->gid)==(-1))
614
TraceExit("Failed to change the group.");
617
if (setuid(srvP->uid) == -1)
618
TraceExit("Failed to change the user.");
621
if (srvP->pidfile!=(-1)) {
624
sprintf(z,"%d",getpid());
625
FileWrite(&srvP->pidfile,z,strlen(z));
626
FileClose(&srvP->pidfile);
630
/* We run the user supplied runfirst after forking, but before accepting
631
connections (helpful when running with threads)
634
runfirst(runfirstArg);
638
/* We can't exist here because ServerRun doesn't return */
639
XMLRPC_ASSERT(FALSE);
645
xmlrpc_server_abyss_run_first(runfirstFn const runfirst,
646
void * const runfirstArg) {
648
runServer(&globalSrv, runfirst, runfirstArg);
654
xmlrpc_server_abyss_run(void) {
655
runServer(&globalSrv, NULL, NULL);
661
xmlrpc_server_abyss_set_handlers(TServer * const srvP,
662
xmlrpc_registry * const registryP) {
664
/* Abyss ought to have a way to register with a handler an argument
665
that gets passed to the handler every time it is called. That's
666
where we should put the registry handle. But we don't find such
667
a thing in Abyss, so we use the global variable 'global_registryP'.
669
global_registryP = registryP;
671
trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
673
ServerAddHandler(srvP, xmlrpc_server_abyss_rpc2_handler);
674
ServerDefaultHandler(srvP, xmlrpc_server_abyss_default_handler);
680
xmlrpc_server_abyss(xmlrpc_env * const envP,
681
const xmlrpc_server_abyss_parms * const parmsP,
682
unsigned int const parm_size) {
684
XMLRPC_ASSERT_ENV_OK(envP);
686
if (parm_size < XMLRPC_APSIZE(registryP))
687
xmlrpc_env_set_fault_formatted(
688
envP, XMLRPC_INTERNAL_ERROR,
689
"You must specify members at least up through "
690
"'registryP' in the server parameters argument. "
691
"That would mean the parameter size would be >= %u "
692
"but you specified a size of %u",
693
XMLRPC_APSIZE(registryP), parm_size);
702
ServerCreate(&srv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
704
ConfReadServerFile(parmsP->config_file_name, &srv);
706
xmlrpc_server_abyss_set_handlers(&srv, parmsP->registryP);
710
if (parm_size >= XMLRPC_APSIZE(runfirst_arg)) {
711
runfirst = parmsP->runfirst;
712
runfirstArg = parmsP->runfirst_arg;
717
runServer(&srv, runfirst, runfirstArg);
723
/*=========================================================================
724
** XML-RPC Server Method Registry
725
**=========================================================================
726
** A simple front-end to our method registry.
729
/* XXX - This variable is *not* currently threadsafe. Once the server has
730
** been started, it must be treated as read-only. */
731
static xmlrpc_registry *builtin_registryP;
734
xmlrpc_server_abyss_init_registry(void) {
736
/* This used to just create the registry and Caller would be
737
responsible for adding the handlers that use it.
739
But that isn't very modular -- the handlers and registry go
740
together; there's no sense in using the built-in registry and
741
not the built-in handlers because if you're custom building
742
something, you can just make your own regular registry. So now
743
we tie them together, and we don't export our handlers.
747
xmlrpc_env_init(&env);
748
builtin_registryP = xmlrpc_registry_new(&env);
749
die_if_fault_occurred(&env);
750
xmlrpc_env_clean(&env);
752
xmlrpc_server_abyss_set_handlers(&globalSrv, builtin_registryP);
758
xmlrpc_server_abyss_registry(void) {
760
/* This is highly deprecated. If you want to mess with a registry,
761
make your own with xmlrpc_registry_new() -- don't mess with the
764
return builtin_registryP;
769
/* A quick & easy shorthand for adding a method. */
771
xmlrpc_server_abyss_add_method (char * const method_name,
772
xmlrpc_method const method,
773
void * const user_data) {
776
xmlrpc_env_init(&env);
777
xmlrpc_registry_add_method(&env, builtin_registryP, NULL, method_name,
779
die_if_fault_occurred(&env);
780
xmlrpc_env_clean(&env);
786
xmlrpc_server_abyss_add_method_w_doc (char * const method_name,
787
xmlrpc_method const method,
788
void * const user_data,
789
char * const signature,
793
xmlrpc_env_init(&env);
794
xmlrpc_registry_add_method_w_doc(
795
&env, builtin_registryP, NULL, method_name,
796
method, user_data, signature, help);
797
die_if_fault_occurred(&env);
798
xmlrpc_env_clean(&env);