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
);