1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**@internal @file nth_server.c
26  * @brief HTTP server.
27  *
28  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
29  *
30  * @date Created: Sat Oct 19 01:37:36 2002 ppessi
31  */
32 
33 #include "config.h"
34 
35 #include <sofia-sip/su_string.h>
36 #include <sofia-sip/su.h>
37 
38 typedef struct server_s server_t;
39 
40 /** @internal SU timer argument pointer type */
41 #define SU_TIMER_ARG_T server_t
42 
43 #include <sofia-sip/http_header.h>
44 #include <sofia-sip/http_status.h>
45 #include <sofia-sip/http_tag.h>
46 
47 #include "sofia-sip/nth.h"
48 
49 #include <sofia-sip/msg_date.h>
50 #include <sofia-sip/msg_addr.h>
51 #include <sofia-sip/su_tagarg.h>
52 
53 #include <sofia-sip/hostdomain.h>
54 
55 /* We are customer of tport_t */
56 #define TP_STACK_T   server_t
57 #define TP_MAGIC_T   void
58 
59 #include <sofia-sip/tport.h>
60 #include <sofia-sip/htable.h>
61 
62 #include <sofia-sip/auth_module.h>
63 
64 #include <stddef.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <stdarg.h>
68 #include <errno.h>
69 #include <assert.h>
70 
71 #ifndef UINT32_MAX
72 #define UINT32_MAX (0xffffffffU)
73 #endif
74 
75 enum { SERVER_TICK = 1000 };
76 
77 #define SERVER_VERSION "nth/" NTH_VERSION
78 
79 HTABLE_DECLARE(hc_htable, hct, nth_client_t);
80 
81 struct server_s
82 {
83   su_home_t          srv_home[1];
84   su_root_t         *srv_root;
85 
86   su_timer_t        *srv_timer;
87   unsigned           srv_now;
88 
89   msg_mclass_t const*srv_mclass;
90   int                srv_mflags;	/**< Message flags */
91 
92   tport_t           *srv_tports;
93   unsigned           srv_queuesize;	/**< Maximum number of queued responses */
94 
95   size_t             srv_max_bodylen;	/**< Maximum accepted length */
96 
97   unsigned           srv_persistent:1;	/**< Allow persistent connections */
98 
99   /** Sites */
100   nth_site_t        *srv_sites;
101 
102   /* Statistics */
103   struct {
104     uint32_t           st_requests;     /**< Received requests */
105     uint32_t           st_responses;    /**< Sent responses */
106     uint32_t           st_bad;		/**< Bad requests */
107   }                  srv_stats[1];
108 
109   http_server_t     *srv_server;      /**< Server header */
110 };
111 
112 struct nth_site_s
113 {
114   nth_site_t          *site_next, **site_prev;
115 
116   nth_site_t          *site_kids;
117 
118   server_t            *site_server;
119   auth_mod_t          *site_auth;
120 
121   url_t               *site_url;
122   char const          *site_path;
123   size_t               site_path_len;
124 
125   nth_request_f       *site_callback;
126   nth_site_magic_t    *site_magic;
127 
128   su_time_t            site_access; /**< Last request served */
129 
130   /** Host header must match with server name */
131   unsigned             site_strict : 1;
132   /** Site can have kids */
133   unsigned             site_isdir : 1;
134   /** Site does not have domain name */
135   unsigned             site_wildcard : 1;
136 };
137 
138 struct nth_request_s
139 {
140   server_t              *req_server;
141 
142   http_method_t        	req_method;
143   char const           *req_method_name;
144   url_t const          *req_url;         /**< RequestURI  */
145   char const           *req_version;
146 
147   tport_t              *req_tport;
148   msg_t		       *req_request;
149   msg_t                *req_response;
150 
151   auth_status_t        *req_as;
152 
153   unsigned short      	req_status;
154   unsigned              req_close : 1; /**< Client asked for close */
155   unsigned              req_in_callback : 1;
156   unsigned              req_destroyed : 1;
157 };
158 
159 /* ====================================================================== */
160 /* Debug log settings */
161 
162 #define SU_LOG   nth_server_log
163 
164 #ifdef SU_DEBUG_H
165 #error <su_debug.h> included directly.
166 #endif
167 #include <sofia-sip/su_debug.h>
168 
169 /**Environment variable determining the debug log level for @b nth
170  * module.
171  *
172  * The NTH_DEBUG environment variable is used to determine the debug
173  * logging level for @b nth module. The default level is 1.
174  *
175  * @sa <sofia-sip/su_debug.h>, nth_server_log, SOFIA_DEBUG
176  */
177 extern char const NTH_DEBUG[];
178 
179 #ifndef SU_DEBUG
180 #define SU_DEBUG 1
181 #endif
182 
183 /**Debug log for @b nth module.
184  *
185  * The nth_server_log is the log object used by @b nth module. The level of
186  * #nth_server_log is set using #NTH_DEBUG environment variable.
187  */
188 su_log_t nth_server_log[] = { SU_LOG_INIT("nth", "NTH_DEBUG", SU_DEBUG) };
189 
190 #if HAVE_FUNC
191 #elif HAVE_FUNCTION
192 #define __func__ __FUNCTION__
193 #else
194 static char const __func__[] = "nth";
195 #endif
196 
197 /* ====================================================================== */
198 /** Server side
199  */
200 static server_t *server_create(url_t const *url,
201 			       tag_type_t tag, tag_value_t value, ...);
202 void server_destroy(server_t *srv);
203 static void server_request(server_t *srv, tport_t *tport, msg_t *msg,
204 				    void *arg, su_time_t now);
205 static nth_site_t **site_get_host(nth_site_t **, char const *host, char const *port);
206 static nth_site_t **site_get_rslot(nth_site_t *parent, char *path,
207 				   char **return_rest);
208 static nth_site_t *site_get_subdir(nth_site_t *parent, char const *path, char const **res);
209 static void server_tport_error(server_t *srv, tport_t *tport,
210 			       int errcode, char const *remote);
211 static msg_t *server_msg_create(server_t *srv, int flags,
212 				char const data[], usize_t dlen,
213 				tport_t const *tp, tp_client_t *tpc);
214 
215 static void server_reply(server_t *srv, tport_t *tport,
216 			 msg_t *request, msg_t *response,
217 			 int status, char const *phrase);
218 
219 static
220 void nth_site_request(server_t *srv,
221 		      nth_site_t *site,
222 		      tport_t *tport,
223 		      msg_t *request,
224 		      http_t *http,
225 		      char const *path,
226 		      msg_t *response);
227 
228 /* ----------------------------------------------------------------------
229  * 5) Site functions
230  */
231 
232 /** Create a http site object.
233  *
234  * The function nth_site_create() allocates and initializes a web site
235  * object. A web site object can be either
236  * - a primary http server (@a parent is NULL),
237  * - a virtual http server (@a address contains hostpart), or
238  * - a site within a server
239  *   (@a address does not have hostpart, only path part).
240  *
241  * @param parent pointer to parent site
242  *               (NULL when creating a primary server object)
243  * @param callback pointer to callback function called when
244  *                 a request is received
245  * @param magic    application context included in callback parameters
246  * @param address  absolute or relative URI specifying the address of
247  *                 site
248  * @param tag, value, ... list of tagged parameters
249  *
250  * @TAGS
251  * If the @a parent is NULL, the list of tagged parameters must contain
252  * NTHTAG_ROOT() used to create the server engine. Tags supported when @a
253  * parent is NULL are NTHTAG_ROOT(), NTHTAG_MCLASS(), TPTAG_REUSE(),
254  * HTTPTAG_SERVER(), and HTTPTAG_SERVER_STR(). All the tags are passed to
255  * tport_tcreate() and tport_tbind(), too.
256  *
257  * @since Support for multiple sites was added to @VERSION_1_12_4
258  */
nth_site_create(nth_site_t * parent,nth_request_f * callback,nth_site_magic_t * magic,url_string_t const * address,tag_type_t tag,tag_value_t value,...)259 nth_site_t *nth_site_create(nth_site_t *parent,
260 			    nth_request_f *callback,
261 			    nth_site_magic_t *magic,
262 			    url_string_t const *address,
263 			    tag_type_t tag, tag_value_t value,
264 			    ...)
265 {
266   nth_site_t *site = NULL, **prev = NULL;
267   su_home_t home[SU_HOME_AUTO_SIZE(256)];
268   url_t *url, url0[1];
269   server_t *srv = NULL;
270   ta_list ta;
271   char *path = NULL;
272   size_t usize;
273   int is_host, is_path, wildcard = 0;
274 
275   su_home_auto(home, sizeof home);
276 
277   if (parent && url_is_string(address)) {
278     char const *s = (char const *)address;
279     size_t sep = strcspn(s, "/:");
280 
281     if (parent->site_path) {
282       /* subpath */
283       url_init(url0, (enum url_type_e)parent->site_url->url_type);
284       url0->url_path = s;
285       address = (url_string_t*)url0;
286     }
287     else if (s[sep] == ':')
288       /* absolute URL with scheme */;
289     else if (s[sep] == '\0' && strchr(s, '.') && host_is_valid(s)) {
290       /* looks like a domain name */;
291       url_init(url0, (enum url_type_e)parent->site_url->url_type);
292       url0->url_host = s;
293       address = (url_string_t*)url0;
294     }
295     else {
296       /* looks like a path */
297       url_init(url0, (enum url_type_e)parent->site_url->url_type);
298       url0->url_path = s;
299       address = (url_string_t*)url0;
300     }
301   }
302 
303   url = url_hdup(home, address->us_url);
304 
305   if (!url || !callback)
306     return NULL;
307 
308   is_host = url->url_host != NULL;
309   is_path = url->url_path != NULL;
310 
311   if (is_host && is_path) {
312     SU_DEBUG_3(("nth_site_create(): virtual host and path simultanously\n" VA_NONE));
313     errno = EINVAL;
314     goto error;
315   }
316 
317   if (!parent && !is_host) {
318     SU_DEBUG_3(("nth_site_create(): host is required\n" VA_NONE));
319     errno = EINVAL;
320     goto error;
321   }
322 
323   if (parent) {
324     if (!parent->site_isdir) {
325       SU_DEBUG_3(("nth_site_create(): invalid parent resource \n" VA_NONE));
326       errno = EINVAL;
327       goto error;
328     }
329 
330     srv = parent->site_server; assert(srv);
331     if (is_host) {
332       prev = site_get_host(&srv->srv_sites, url->url_host, url->url_port);
333 
334       if (prev == NULL) {
335 	SU_DEBUG_3(("nth_site_create(): host %s:%s already exists\n",
336 		    url->url_host, url->url_port ? url->url_port : ""));
337 	errno = EEXIST;
338 	goto error;
339       }
340     }
341     else {
342       size_t i, j;
343 
344       path = (char *)url->url_path;
345 
346       if (!path) {
347           SU_DEBUG_3(("nth_site_create(): invalid url\n" VA_NONE));
348           errno = EINVAL;
349           goto error;
350       }
351 
352       while (path[0] == '/')
353 	path++;
354 
355       /* Remove duplicate // */
356       for (i = j = 0; path[i];) {
357 	while (path[i] == '/' && path[i + 1] == '/')
358 	  i++;
359 	path[j++] = path[i++];
360       }
361       path[j] = path[i];
362 
363       url = url0, *url = *parent->site_url;
364 
365       if (url->url_path) {
366 	url->url_path = su_strcat(home, url->url_path, path);
367 	if (!url->url_path)
368 	  goto error;
369 	path = (char *)url->url_path + strlen(parent->site_url->url_path);
370       }
371       else
372 	url->url_path = path;
373 
374       prev = site_get_rslot(parent, path, &path);
375 
376       if (!prev || path[0] == '\0') {
377 	SU_DEBUG_3(("nth_site_create(): directory \"%s\" already exists\n",
378 		    url->url_path));
379 	errno = EEXIST;
380 	goto error;
381       }
382     }
383   }
384 
385   if (!parent) {
386     if (strcmp(url->url_host, "*") == 0 ||
387 	host_cmp(url->url_host, "0.0.0.0") == 0 ||
388 	host_cmp(url->url_host, "::") == 0)
389       wildcard = 1, url->url_host = "*";
390   }
391 
392   usize = sizeof(*url) + url_xtra(url);
393 
394   ta_start(ta, tag, value);
395 
396   if (!parent) {
397     srv = server_create(url, ta_tags(ta));
398     prev = &srv->srv_sites;
399   }
400 
401   if (srv && (site = su_zalloc(srv->srv_home, (sizeof *site) + usize))) {
402     site->site_url = (url_t *)(site + 1);
403     url_dup((void *)(site->site_url + 1), usize - sizeof(*url),
404 	    site->site_url, url);
405 
406     assert(prev);
407     if ((site->site_next = *prev))
408       site->site_next->site_prev = &site->site_next;
409     *prev = site, site->site_prev = prev;
410     site->site_server = srv;
411 
412     if (path) {
413       size_t path_len;
414 
415       site->site_path = site->site_url->url_path + (path - url->url_path);
416       path_len = strlen(site->site_path); assert(path_len > 0);
417       if (path_len > 0 && site->site_path[path_len - 1] == '/')
418 	path_len--, site->site_isdir = 1;
419       site->site_path_len = path_len;
420     }
421     else {
422       site->site_isdir = is_host;
423       site->site_path = "";
424       site->site_path_len = 0;
425     }
426 
427     site->site_wildcard = wildcard;
428     site->site_callback = callback;
429     site->site_magic = magic;
430 
431     if (parent)
432       site->site_auth = parent->site_auth;
433 
434     nth_site_set_params(site, ta_tags(ta));
435   }
436 
437   ta_end(ta);
438 
439  error:
440   su_home_deinit(home);
441   return site;
442 }
443 
nth_site_destroy(nth_site_t * site)444 void nth_site_destroy(nth_site_t *site)
445 {
446   if (site == NULL)
447     return;
448 
449   if (site->site_auth)
450     auth_mod_unref(site->site_auth), site->site_auth = NULL;
451 
452   if (site->site_server->srv_sites == site) {
453     server_destroy(site->site_server);
454   }
455 }
456 
457 
nth_site_magic(nth_site_t const * site)458 nth_site_magic_t *nth_site_magic(nth_site_t const *site)
459 {
460   return site ? site->site_magic : NULL;
461 }
462 
463 
nth_site_bind(nth_site_t * site,nth_request_f * callback,nth_site_magic_t * magic)464 void nth_site_bind(nth_site_t *site,
465 		   nth_request_f *callback,
466 		   nth_site_magic_t *magic)
467 {
468   if (site) {
469     site->site_callback = callback;
470     site->site_magic = magic;
471   }
472 }
473 
474 
475 /** Get the site URL. @NEW_1_12_4. */
nth_site_url(nth_site_t const * site)476 url_t const *nth_site_url(nth_site_t const *site)
477 {
478   return site ? site->site_url : NULL;
479 }
480 
481 /** Return server name and version */
nth_site_server_version(void)482 char const *nth_site_server_version(void)
483 {
484   return "nth/" NTH_VERSION;
485 }
486 
487 /** Get the time last time served. @NEW_1_12_4. */
nth_site_access_time(nth_site_t const * site)488 su_time_t nth_site_access_time(nth_site_t const *site)
489 {
490   su_time_t const never = { 0, 0 };
491 
492   return site ? site->site_access : never;
493 }
494 
nth_site_set_params(nth_site_t * site,tag_type_t tag,tag_value_t value,...)495 int nth_site_set_params(nth_site_t *site,
496 			tag_type_t tag, tag_value_t value, ...)
497 {
498   int n;
499   ta_list ta;
500 
501   server_t *server;
502   int master;
503   msg_mclass_t const *mclass;
504   int mflags;
505   auth_mod_t *am;
506 
507   if (site == NULL)
508     return (errno = EINVAL), -1;
509 
510   server = site->site_server;
511   master = site == server->srv_sites;
512   am = site->site_auth;
513 
514   mclass = server->srv_mclass;
515   mflags = server->srv_mflags;
516 
517   ta_start(ta, tag, value);
518 
519   n = tl_gets(ta_args(ta),
520 	      TAG_IF(master, NTHTAG_MCLASS_REF(mclass)),
521 	      TAG_IF(master, NTHTAG_MFLAGS_REF(mflags)),
522 	      NTHTAG_AUTH_MODULE_REF(am),
523 	      TAG_END());
524 
525   if (n > 0) {
526     if (mclass)
527       server->srv_mclass = mclass;
528     else
529       server->srv_mclass = http_default_mclass();
530     server->srv_mflags = mflags;
531     auth_mod_ref(am), auth_mod_unref(site->site_auth), site->site_auth = am;
532   }
533 
534   ta_end(ta);
535 
536   return n;
537 }
538 
nth_site_get_params(nth_site_t const * site,tag_type_t tag,tag_value_t value,...)539 int nth_site_get_params(nth_site_t const *site,
540 			tag_type_t tag, tag_value_t value, ...)
541 {
542   int n;
543   ta_list ta;
544   server_t *server;
545   int master;
546   msg_mclass_t const *mclass;
547 
548   if (site == NULL)
549     return (errno = EINVAL), -1;
550 
551   server = site->site_server;
552   master = site == server->srv_sites;
553 
554   if (master && server->srv_mclass != http_default_mclass())
555     mclass = server->srv_mclass;
556   else
557     mclass = NULL;
558 
559   ta_start(ta, tag, value);
560 
561   n = tl_tgets(ta_args(ta),
562 	       TAG_IF(master, NTHTAG_MCLASS(mclass)),
563 	       TAG_IF(master, NTHTAG_MFLAGS(server->srv_mflags)),
564 	       TAG_END());
565 
566   ta_end(ta);
567 
568   return n;
569 }
570 
nth_site_get_stats(nth_site_t const * site,tag_type_t tag,tag_value_t value,...)571 int nth_site_get_stats(nth_site_t const *site,
572 		       tag_type_t tag, tag_value_t value, ...)
573 {
574   int n;
575   ta_list ta;
576 
577   if (site == NULL)
578     return (errno = EINVAL), -1;
579 
580   ta_start(ta, tag, value);
581 
582   n = tl_tgets(ta_args(ta),
583 	       TAG_END());
584 
585   ta_end(ta);
586 
587   return n;
588 }
589 
590 static
site_get_host(nth_site_t ** list,char const * host,char const * port)591 nth_site_t **site_get_host(nth_site_t **list, char const *host, char const *port)
592 {
593   nth_site_t *site;
594 
595   assert(host);
596 
597   for (; (site = *list); list = &site->site_next) {
598     if (host_cmp(host, site->site_url->url_host) == 0 &&
599 	su_strcmp(port, site->site_url->url_port) == 0) {
600       break;
601     }
602   }
603 
604   return list;
605 }
606 
607 /** Find a place to insert site from the hierarchy.
608  *
609  * A resource can be either a 'dir' (name ends with '/') or 'file'.
610  * When a resource
611  */
612 static
site_get_rslot(nth_site_t * parent,char * path,char ** return_rest)613 nth_site_t **site_get_rslot(nth_site_t *parent, char *path,
614 			    char **return_rest)
615 {
616   nth_site_t *site, **prev;
617   size_t len;
618   int cmp;
619 
620   assert(path);
621 
622   if (path[0] == '\0')
623     return errno = EEXIST, NULL;
624 
625   for (prev = &parent->site_kids; (site = *prev); prev = &site->site_next) {
626     cmp = strncmp(path, site->site_path, len = site->site_path_len);
627     if (cmp > 0)
628       break;
629     if (cmp < 0)
630       continue;
631     if (path[len] == '\0') {
632       if (site->site_isdir)
633 	return *return_rest = path, prev;
634       return errno = EEXIST, NULL;
635     }
636     if (path[len] != '/' || site->site_path[len] != '/')
637       continue;
638 
639     while (path[++len] == '/')
640       ;
641 
642     return site_get_rslot(site, path + len, return_rest);
643   }
644 
645   *return_rest = path;
646 
647   return prev;
648 }
649 
650 static char const site_nodir_match[] = "";
651 
652 /** Find a subdir from site hierarchy */
653 static
site_get_subdir(nth_site_t * parent,char const * path,char const ** return_rest)654 nth_site_t *site_get_subdir(nth_site_t *parent,
655 			    char const *path,
656 			    char const **return_rest)
657 {
658   nth_site_t *site;
659   size_t len;
660   int cmp;
661 
662   assert(path);
663 
664   while (path[0] == '/')	/* Skip multiple slashes */
665     path++;
666 
667   if (path[0] == '\0')
668     return *return_rest = path, parent;
669 
670   for (site = parent->site_kids; site; site = site->site_next) {
671     cmp = strncmp(path, site->site_path, len = site->site_path_len);
672     if (cmp > 0)
673       break;
674     if (cmp < 0)
675       continue;
676     if (path[len] == '\0')
677       return *return_rest = site_nodir_match, site;
678     if (site->site_path[len] == '/' && path[len] == '/')
679       return site_get_subdir(site, path + len + 1, return_rest);
680   }
681 
682   return *return_rest = path, parent;
683 }
684 
685 
686 /* ----------------------------------------------------------------------
687  * Server functions
688  */
689 
690 static char const * const http_tports[] =
691   {
692     "tcp", "tls", NULL
693   };
694 
695 static char const * const http_no_tls_tports[] = { "tcp", NULL };
696 
697 static tp_stack_class_t nth_server_class[1] =
698   {{
699     sizeof(nth_server_class),
700     server_request,
701     server_tport_error,
702     server_msg_create
703   }};
704 
server_create(url_t const * url,tag_type_t tag,tag_value_t value,...)705 server_t *server_create(url_t const *url,
706 			tag_type_t tag, tag_value_t value, ...)
707 {
708   server_t *srv;
709   msg_mclass_t const *mclass = NULL;
710   tp_name_t tpn[1] = {{ NULL }};
711   su_root_t *root = NULL;
712   http_server_t const *server = NULL;
713   int persistent = 0;
714   char const *server_str = SERVER_VERSION;
715   ta_list ta;
716 
717   ta_start(ta, tag, value);
718   tl_gets(ta_args(ta),
719 	  NTHTAG_ROOT_REF(root),
720 	  NTHTAG_MCLASS_REF(mclass),
721 	  TPTAG_REUSE_REF(persistent),
722 	  HTTPTAG_SERVER_REF(server),
723 	  HTTPTAG_SERVER_STR_REF(server_str),
724 	  TAG_END());
725 
726   if (!root || !url ||
727       (url->url_type != url_http && url->url_type != url_https)
728       || !(srv = su_home_new(sizeof(*srv)))) {
729     ta_end(ta);
730     return NULL;
731   }
732 
733   tpn->tpn_proto = url_tport_default((enum url_type_e)url->url_type);
734   tpn->tpn_canon = url->url_host;
735   tpn->tpn_host =  url->url_host;
736   tpn->tpn_port = url_port(url);
737 
738   srv->srv_tports = tport_tcreate(srv, nth_server_class, root,
739 				  TPTAG_IDLE(600000),
740 				  TPTAG_TIMEOUT(300000),
741 				  ta_tags(ta));
742 
743   srv->srv_persistent = persistent;
744   srv->srv_max_bodylen = 1 << 30; /* 1 GB */
745 
746   if (tport_tbind(srv->srv_tports, tpn, http_tports,
747 		  TAG_END()) >= 0 ||
748       tport_tbind(srv->srv_tports, tpn, http_no_tls_tports,
749 		  TAG_END()) >= 0) {
750     srv->srv_root = root;
751     srv->srv_mclass = mclass ? mclass : http_default_mclass();
752     srv->srv_mflags = MSG_DO_CANONIC;
753 
754     if (server)
755       srv->srv_server = http_server_dup(srv->srv_home, server);
756     else
757       srv->srv_server = http_server_make(srv->srv_home, server_str);
758 
759     tport_get_params(srv->srv_tports,
760 		     TPTAG_QUEUESIZE_REF(srv->srv_queuesize),
761 		     TAG_END());
762   }
763   else {
764     SU_DEBUG_1(("nth_server_create: cannot bind transports "
765 		URL_FORMAT_STRING "\n",
766 		URL_PRINT_ARGS(url)));
767     server_destroy(srv), srv = NULL;
768   }
769 
770   ta_end(ta);
771 
772   return srv;
773 }
774 
server_destroy(server_t * srv)775 void server_destroy(server_t *srv)
776 {
777   tport_destroy(srv->srv_tports);
778   su_timer_destroy(srv->srv_timer);
779   su_home_unref(srv->srv_home);
780 }
781 
782 /** Process incoming request message */
783 static
server_request(server_t * srv,tport_t * tport,msg_t * request,void * arg,su_time_t now)784 void server_request(server_t *srv,
785 		    tport_t *tport,
786 		    msg_t *request,
787 		    void *arg,
788 		    su_time_t now)
789 {
790   nth_site_t *site = NULL, *subsite = NULL;
791   msg_t *response;
792   http_t *http = http_object(request);
793   http_host_t *h;
794   char const *host, *port, *path, *subpath = NULL;
795 
796   /* Disable streaming */
797   if (msg_is_streaming(request)) {
798     msg_set_streaming(request, (enum msg_streaming_status)0);
799     return;
800   }
801 
802   /* Create a response message */
803   response = server_msg_create(srv, 0, NULL, 0, NULL, NULL);
804   tport_tqueue(tport, response, TAG_END());
805 
806   if (http && http->http_flags & MSG_FLG_TIMEOUT) {
807     server_reply(srv, tport, request, response, 400, "Request timeout");
808     return;
809   } else if (http && http->http_flags & MSG_FLG_TOOLARGE) {
810     server_reply(srv, tport, request, response, HTTP_413_ENTITY_TOO_LARGE);
811     return;
812   } else if (!http || !http->http_request ||
813 	     (http->http_flags & MSG_FLG_ERROR)) {
814     server_reply(srv, tport, request, response, HTTP_400_BAD_REQUEST);
815     return;
816   }
817 
818   if (http->http_request->rq_version != http_version_1_0 &&
819       http->http_request->rq_version != http_version_1_1) {
820     server_reply(srv, tport, request, response, HTTP_505_HTTP_VERSION);
821     return;
822   }
823 
824   h = http->http_host;
825 
826   if (h) {
827     host = h->h_host, port = h->h_port;
828   }
829   else if (http->http_request->rq_url->url_host) {
830     host = http->http_request->rq_url->url_host;
831     port = http->http_request->rq_url->url_port;
832   }
833   else
834     host = NULL, port = NULL;
835 
836   path = http->http_request->rq_url->url_path;
837 
838   if (host)
839     site = *site_get_host(&srv->srv_sites, host, port);
840 
841   if (site == NULL && !srv->srv_sites->site_strict)
842     site = srv->srv_sites;
843 
844   if (path == NULL)
845     path = "";
846 
847   if (path[0])
848     subsite = site_get_subdir(site, path, &subpath);
849 
850   if (subsite)
851     subsite->site_access = now;
852   else if (site)
853     site->site_access = now;
854 
855   if (site && subsite && subsite->site_isdir && subpath == site_nodir_match) {
856     /* Answer with 301 */
857     http_location_t loc[1];
858     http_location_init(loc);
859 
860     *loc->loc_url = *site->site_url;
861 
862     if (site->site_wildcard) {
863       if (http->http_host) {
864 	loc->loc_url->url_host = http->http_host->h_host;
865 	loc->loc_url->url_port = http->http_host->h_port;
866       }
867       else {
868 	tp_name_t const *tpn = tport_name(tport); assert(tpn);
869 	loc->loc_url->url_host = tpn->tpn_canon;
870 	if (strcmp(url_port_default((enum url_type_e)loc->loc_url->url_type), tpn->tpn_port))
871 	  loc->loc_url->url_port = tpn->tpn_port;
872       }
873     }
874 
875     loc->loc_url->url_root = 1;
876     loc->loc_url->url_path = subsite->site_url->url_path;
877 
878     msg_header_add_dup(response, NULL, (msg_header_t *)loc);
879 
880     server_reply(srv, tport, request, response, HTTP_301_MOVED_PERMANENTLY);
881   }
882   else if (subsite)
883     nth_site_request(srv, subsite, tport, request, http, subpath, response);
884   else if (site)
885     nth_site_request(srv, site, tport, request, http, path, response);
886   else
887     /* Answer with 404 */
888     server_reply(srv, tport, request, response, HTTP_404_NOT_FOUND);
889 }
890 
server_tport_error(server_t * srv,tport_t * tport,int errcode,char const * remote)891 static void server_tport_error(server_t *srv,
892 				   tport_t *tport,
893 				   int errcode,
894 				   char const *remote)
895 {
896   su_log("\nth: tport: %s%s%s\n",
897 	 remote ? remote : "", remote ? ": " : "",
898 	 su_strerror(errcode));
899 }
900 
901 /** Respond without creating a request structure */
server_reply(server_t * srv,tport_t * tport,msg_t * request,msg_t * response,int status,char const * phrase)902 static void server_reply(server_t *srv, tport_t *tport,
903 			 msg_t *request, msg_t *response,
904 			 int status, char const *phrase)
905 {
906   http_t *http;
907   http_payload_t *pl;
908   int close;
909   http_status_t st[1];
910   char const *req_version = NULL;
911 
912   if (status < 200 || status >= 600)
913     status = 500, phrase = http_500_internal_server;
914 
915   http = http_object(request);
916 
917   if (http && http->http_request)
918     req_version = http->http_request->rq_version;
919 
920   close = status >= 200 &&
921     (!srv->srv_persistent
922      || status == 400
923      || (http && http->http_request &&
924 	 http->http_request->rq_version != http_version_1_1)
925      || (http && http->http_connection &&
926 	 msg_params_find(http->http_connection->k_items, "close")));
927 
928   msg_destroy(request);
929 
930   http = http_object(response);
931 
932   pl = http_payload_format(msg_home(response),
933 			   "<html>\n"
934 			   "<head><title>%u %s</title></head>\n"
935 			   "<body><h2>%u %s</h2></body>\n"
936 			   "</html>\n", status, phrase, status, phrase);
937 
938   msg_header_insert(response, (msg_pub_t *)http, (msg_header_t *)pl);
939 
940   if (req_version != http_version_0_9) {
941     http_status_init(st);
942     st->st_version = http_version_1_1;
943     st->st_status = status;
944     st->st_phrase = phrase;
945 
946     http_add_tl(response, http,
947 		HTTPTAG_STATUS(st),
948 		HTTPTAG_SERVER(srv->srv_server),
949 		HTTPTAG_CONTENT_TYPE_STR("text/html"),
950 		HTTPTAG_SEPARATOR_STR("\r\n"),
951 		TAG_IF(close, HTTPTAG_CONNECTION_STR("close")),
952 		TAG_END());
953 
954     msg_serialize(response, (msg_pub_t *)http);
955   } else {
956     /* Just send the response */
957     *msg_chain_head(response) = (msg_header_t *)pl;
958     close = 1;
959   }
960 
961   if (tport_tqsend(tport, response, NULL,
962 		   TPTAG_CLOSE_AFTER(close),
963 		   TAG_END()) == -1) {
964     SU_DEBUG_3(("server_reply(): cannot queue response\n" VA_NONE));
965     tport_shutdown(tport, 2);
966   }
967 
968   msg_destroy(response);
969 }
970 
971 /** Create a new message for transport */
972 static
server_msg_create(server_t * srv,int flags,char const data[],usize_t dlen,tport_t const * tp,tp_client_t * tpc)973 msg_t *server_msg_create(server_t *srv, int flags,
974 			 char const data[], usize_t dlen,
975 			 tport_t const *tp, tp_client_t *tpc)
976 {
977   msg_t *msg = msg_create(srv->srv_mclass, srv->srv_mflags | flags);
978 
979   return msg;
980 }
981 
982 /* ----------------------------------------------------------------------
983  * 6) Server transactions
984  */
985 
986 struct auth_info
987 {
988   nth_site_t *site;
989   nth_request_t *req;
990   http_t const *http;
991   char const *path;
992 };
993 
994 static void nth_authentication_result(void *ai0, auth_status_t *as);
995 
996 static
nth_site_request(server_t * srv,nth_site_t * site,tport_t * tport,msg_t * request,http_t * http,char const * path,msg_t * response)997 void nth_site_request(server_t *srv,
998 		      nth_site_t *site,
999 		      tport_t *tport,
1000 		      msg_t *request,
1001 		      http_t *http,
1002 		      char const *path,
1003 		      msg_t *response)
1004 {
1005   auth_mod_t *am = site->site_auth;
1006   nth_request_t *req;
1007   auth_status_t *as;
1008   struct auth_info *ai;
1009   size_t size = (am ? (sizeof *as) + (sizeof *ai) : 0) + (sizeof *req);
1010   int status;
1011 
1012   req = su_zalloc(srv->srv_home, size);
1013 
1014   if (req == NULL) {
1015     server_reply(srv, tport, request, response, HTTP_500_INTERNAL_SERVER);
1016     return;
1017   }
1018 
1019   if (am)
1020     as = auth_status_init(req + 1, sizeof *as), ai = (void *)(as + 1);
1021   else
1022     as = NULL, ai = NULL;
1023 
1024   req->req_server = srv;
1025   req->req_method = http->http_request->rq_method;
1026   req->req_method_name = http->http_request->rq_method_name;
1027   req->req_url = http->http_request->rq_url;
1028   req->req_version = http->http_request->rq_version;
1029 
1030   req->req_tport = tport_incref(tport);
1031   req->req_request = request;
1032   req->req_response = response;
1033 
1034   req->req_status = 100;
1035   req->req_close =
1036     !srv->srv_persistent
1037     || http->http_request->rq_version != http_version_1_1
1038     || (http->http_connection &&
1039 	msg_params_find(http->http_connection->k_items, "close"));
1040 
1041   if (am) {
1042     static auth_challenger_t const http_server_challenger[] =
1043       {{ HTTP_401_UNAUTHORIZED, http_www_authenticate_class }};
1044 
1045     req->req_as = as;
1046 
1047     as->as_method = http->http_request->rq_method_name;
1048     as->as_uri = path;
1049 
1050     if (http->http_payload) {
1051       as->as_body = http->http_payload->pl_data;
1052       as->as_bodylen = http->http_payload->pl_len;
1053     }
1054 
1055     auth_mod_check_client(am, as,
1056 			  http->http_authorization,
1057 			  http_server_challenger);
1058 
1059     if (as->as_status == 100) {
1060       /* Stall transport - do not read more requests */
1061       if (tport_queuelen(tport) * 2 >= srv->srv_queuesize)
1062 	tport_stall(tport);
1063 
1064       as->as_callback = nth_authentication_result;
1065       as->as_magic = ai;
1066       ai->site = site;
1067       ai->req = req;
1068       ai->http = http;
1069       ai->path = path;
1070       return;
1071     }
1072     else if (as->as_status) {
1073       assert(as->as_status >= 200);
1074       nth_request_treply(req, as->as_status, as->as_phrase,
1075 			 HTTPTAG_HEADER((http_header_t *)as->as_response),
1076 			 HTTPTAG_HEADER((http_header_t *)as->as_info),
1077 			 TAG_END());
1078       nth_request_destroy(req);
1079       return;
1080     }
1081   }
1082 
1083   req->req_in_callback = 1;
1084   status = site->site_callback(site->site_magic, site, req, http, path);
1085   req->req_in_callback = 0;
1086 
1087   if (status != 0 && (status < 100 || status >= 600))
1088     status = 500;
1089 
1090   if (status != 0 && req->req_status < 200) {
1091     nth_request_treply(req, status, NULL, TAG_END());
1092   }
1093 
1094   if (req->req_status < 100) {
1095     /* Stall transport - do not read more requests */
1096     if (tport_queuelen(tport) * 2 >= srv->srv_queuesize)
1097       tport_stall(tport);
1098   }
1099 
1100   if (status >= 200 || req->req_destroyed)
1101     nth_request_destroy(req);
1102 }
1103 
nth_authentication_result(void * ai0,auth_status_t * as)1104 static void nth_authentication_result(void *ai0, auth_status_t *as)
1105 {
1106   struct auth_info *ai = ai0;
1107   nth_request_t *req = ai->req;
1108   int status;
1109 
1110   if (as->as_status != 0) {
1111     assert(as->as_status >= 300);
1112     nth_request_treply(req, status = as->as_status, as->as_phrase,
1113 		       HTTPTAG_HEADER((http_header_t *)as->as_response),
1114 		       TAG_END());
1115   }
1116   else {
1117     req->req_in_callback = 1;
1118     status = ai->site->site_callback(ai->site->site_magic,
1119 				     ai->site,
1120 				     ai->req,
1121 				     ai->http,
1122 				     ai->path);
1123     req->req_in_callback = 0;
1124 
1125     if (status != 0 && (status < 100 || status >= 600))
1126       status = 500;
1127 
1128     if (status != 0 && req->req_status < 200) {
1129       nth_request_treply(req, status, NULL, TAG_END());
1130     }
1131   }
1132 
1133   if (status >= 200 || req->req_destroyed)
1134     nth_request_destroy(req);
1135 }
1136 
nth_request_destroy(nth_request_t * req)1137 void nth_request_destroy(nth_request_t *req)
1138 {
1139   if (req == NULL)
1140     return;
1141 
1142   if (req->req_status < 200)
1143     nth_request_treply(req, HTTP_500_INTERNAL_SERVER, TAG_END());
1144 
1145   req->req_destroyed = 1;
1146 
1147   if (req->req_in_callback)
1148     return;
1149 
1150   if (req->req_as)
1151     su_home_deinit(req->req_as->as_home);
1152 
1153   tport_decref(&req->req_tport), req->req_tport = NULL;
1154   msg_destroy(req->req_request), req->req_request = NULL;
1155   msg_destroy(req->req_response), req->req_response = NULL;
1156   su_free(req->req_server->srv_home, req);
1157 }
1158 
1159 /** Return request authentication status.
1160  *
1161  * @param req pointer to HTTP request object
1162  *
1163  * @retval Status code
1164  *
1165  * @since New in @VERSION_1_12_4
1166  */
nth_request_status(nth_request_t const * req)1167 int nth_request_status(nth_request_t const *req)
1168 {
1169   return req ? req->req_status : 400;
1170 }
1171 
1172 /** Return request authentication status.
1173  *
1174  * @param req pointer to HTTP request object
1175  *
1176  * @retval Pointer to authentication status struct
1177  *
1178  * @note The authentication status struct is freed when the #nth_request_t
1179  * object is destroyed.
1180  *
1181  * @since New in @VERSION_1_12_4
1182  *
1183  * @sa AUTH
1184  */
nth_request_auth(nth_request_t const * req)1185 auth_status_t *nth_request_auth(nth_request_t const *req)
1186 {
1187   return req ? req->req_as : NULL;
1188 }
1189 
nth_request_method(nth_request_t const * req)1190 http_method_t nth_request_method(nth_request_t const *req)
1191 {
1192   return req ? req->req_method : http_method_invalid;
1193 }
1194 
nth_request_message(nth_request_t * req)1195 msg_t *nth_request_message(nth_request_t *req)
1196 {
1197   msg_t *retval = NULL;
1198 
1199   if (req)
1200     retval = msg_ref_create(req->req_request);
1201 
1202   return retval;
1203 }
1204 
nth_request_treply(nth_request_t * req,int status,char const * phrase,tag_type_t tag,tag_value_t value,...)1205 int nth_request_treply(nth_request_t *req,
1206 		       int status, char const *phrase,
1207 		       tag_type_t tag, tag_value_t value, ...)
1208 {
1209   msg_t *response, *next = NULL;
1210   http_t *http;
1211   int retval = -1;
1212   int req_close, close;
1213   ta_list ta;
1214   http_header_t const *as_info = NULL;
1215 
1216   if (req == NULL || status < 100 || status >= 600) {
1217     return -1;
1218   }
1219 
1220   response = req->req_response;
1221   http = http_object(response);
1222 
1223   if (status >= 200 && req->req_as)
1224     as_info = (http_header_t const *)req->req_as->as_info;
1225 
1226   ta_start(ta, tag, value);
1227 
1228   http_add_tl(response, http,
1229 	      HTTPTAG_SERVER(req->req_server->srv_server),
1230 	      HTTPTAG_HEADER(as_info),
1231 	      ta_tags(ta));
1232 
1233   if (http->http_payload && !http->http_content_length) {
1234     http_content_length_t *l;
1235     http_payload_t *pl;
1236     size_t len = 0;
1237 
1238     for (pl = http->http_payload; pl; pl = pl->pl_next)
1239       len += pl->pl_len;
1240 
1241     if (len > UINT32_MAX)
1242       goto fail;
1243 
1244     l = http_content_length_create(msg_home(response), (uint32_t)len);
1245 
1246     msg_header_insert(response, (msg_pub_t *)http, (msg_header_t *)l);
1247   }
1248 
1249   if (req->req_method == http_method_head && http->http_payload) {
1250     http_payload_t *pl;
1251 
1252     for (pl = http->http_payload; pl; pl = pl->pl_next)
1253       msg_header_remove(response, (msg_pub_t *)http, (msg_header_t *)pl);
1254   }
1255 
1256   http_complete_response(response, status, phrase,
1257 			 http_object(req->req_request));
1258 
1259   if (!http->http_date) {
1260     http_date_t date[1];
1261     http_date_init(date)->d_time = msg_now();
1262     msg_header_add_dup(response, (msg_pub_t *)http, (msg_header_t*)date);
1263   }
1264 
1265   if (status < 200) {
1266     close = 0;
1267     next = server_msg_create(req->req_server, 0, NULL, 0, NULL, NULL);
1268   }
1269   else {
1270     req_close = req->req_close;
1271 
1272     close = (http->http_connection &&
1273 	     msg_params_find(http->http_connection->k_items, "close"));
1274 
1275     if (req_close && !close && status >= 200) {
1276       close = 1;
1277       http_add_tl(response, http, HTTPTAG_CONNECTION_STR("close"), TAG_END());
1278     }
1279   }
1280 
1281   msg_serialize(response, (msg_pub_t *)http);
1282 
1283   retval = tport_tqsend(req->req_tport, response, next,
1284 			TAG_IF(close, TPTAG_CLOSE_AFTER(1)),
1285 			ta_tags(ta));
1286 
1287  fail:
1288   ta_end(ta);
1289 
1290   if (retval == 0)
1291     req->req_status = status;
1292 
1293   return retval;
1294 }
1295