Move ACTION_VANILLA_WAFER handling from jcc's chat() into
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index e82e95c..e5eb7f5 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.140 2007/07/21 11:51:36 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.151 2007/09/29 10:21:16 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -33,6 +33,53 @@ const char jcc_rcs[] = "$Id: jcc.c,v 1.140 2007/07/21 11:51:36 fabiankeil Exp $"
  *
  * Revisions   :
  *    $Log: jcc.c,v $
+ *    Revision 1.151  2007/09/29 10:21:16  fabiankeil
+ *    - Move get_filter_function() from jcc.c to filters.c
+ *      so the filter functions can be static.
+ *    - Don't bother filtering body-less responses.
+ *
+ *    Revision 1.150  2007/09/28 16:39:29  fabiankeil
+ *    Execute content filters through execute_content_filter().
+ *
+ *    Revision 1.149  2007/09/04 15:08:48  fabiankeil
+ *    Initialize req to NULL to make sure it's defined if the
+ *    first read_socket() call fails. Reported by icmp30.
+ *
+ *    Revision 1.148  2007/08/26 16:47:13  fabiankeil
+ *    Add Stephen Gildea's --pre-chroot-nslookup patch [#1276666],
+ *    extensive comments moved to user manual.
+ *
+ *    Revision 1.147  2007/08/25 14:42:40  fabiankeil
+ *    Don't crash if a broken header filter wiped out the request line.
+ *
+ *    Revision 1.146  2007/08/20 17:09:32  fabiankeil
+ *    Fix byte_count calculation in case of flushes
+ *    and don't parse the server headers a second time.
+ *
+ *    Revision 1.145  2007/08/19 13:13:31  fabiankeil
+ *    - If there's a connection problem after we already forwarded
+ *      parts of the original content, just hang up. Fixes BR#1776724.
+ *    - Fix warnings about unused code on mingw32.
+ *    - In case of flushes, calculate the byte count
+ *      less incorrectly (I think).
+ *
+ *    Revision 1.144  2007/08/11 14:43:22  fabiankeil
+ *    Add some more prototypes for static functions.
+ *
+ *    Revision 1.143  2007/08/05 13:58:19  fabiankeil
+ *    Comment out request_contains_null_bytes() until it's used again.
+ *
+ *    Revision 1.142  2007/08/05 13:50:26  fabiankeil
+ *    #1763173 from Stefan Huehner: s@const static@static const@
+ *    and declare some more functions static.
+ *
+ *    Revision 1.141  2007/08/04 09:56:23  fabiankeil
+ *    - Log rejected CONNECT requests with LOG_LEVEL_INFO
+ *      and explain why they were rejected in the first place.
+ *    - Fix the LOG_LEVEL_CLF message for crunches of unallowed
+ *      CONNECT requests. The request line was missing.
+ *    - Add two more XXX reminders as we don't have enough already.
+ *
  *    Revision 1.140  2007/07/21 11:51:36  fabiankeil
  *    As Hal noticed, checking dispatch_cgi() as the last cruncher
  *    looks like a bug if CGI requests are blocked unintentionally,
@@ -950,8 +997,28 @@ int urls_rejected = 0;     /* total nr of urls rejected */
 int g_terminate = 0;
 #endif
 
-static void listen_loop(void);
+#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA)
+static void sig_handler(int the_signal);
+#endif
+static int client_protocol_is_unsupported(const struct client_state *csp, char *req);
+static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers);
+static jb_err get_server_headers(struct client_state *csp);
+static const char *crunch_reason(const struct http_response *rsp);
+static void send_crunch_response(struct client_state *csp, struct http_response *rsp);
+/*
+ * static int request_contains_null_bytes(const struct client_state *csp, char *buf, int len);
+ */
+static void build_request_line(struct client_state *csp, const struct forward_spec *fwd, char **request_line);
+static jb_err change_request_destination(struct client_state *csp);
 static void chat(struct client_state *csp);
+static void serve(struct client_state *csp);
+#if defined(unix)
+static void usage(const char *myname);
+#endif
+static void initialize_mutexes(void);
+static jb_socket bind_port_helper(struct configuration_spec *config);
+static void listen_loop(void);
+
 #ifdef AMIGA
 void serve(struct client_state *csp);
 #else /* ifndef AMIGA */
@@ -998,48 +1065,38 @@ const char *pidfile = NULL;
 int received_hup_signal = 0;
 #endif /* defined unix */
 
-/* The vanilla wafer. */
-static const char VANILLA_WAFER[] =
-   "NOTICE=TO_WHOM_IT_MAY_CONCERN_"
-   "Do_not_send_me_any_copyrighted_information_other_than_the_"
-   "document_that_I_am_requesting_or_any_of_its_necessary_components._"
-   "In_particular_do_not_send_me_any_cookies_that_"
-   "are_subject_to_a_claim_of_copyright_by_anybody._"
-   "Take_notice_that_I_refuse_to_be_bound_by_any_license_condition_"
-   "(copyright_or_otherwise)_applying_to_any_cookie._";
-
 /* HTTP snipplets. */
-const static char CSUCCEED[] =
+static const char CSUCCEED[] =
    "HTTP/1.0 200 Connection established\n"
    "Proxy-Agent: Privoxy/" VERSION "\r\n\r\n";
 
-const static char CHEADER[] =
+static const char CHEADER[] =
    "HTTP/1.0 400 Invalid header received from browser\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "Content-Type: text/plain\r\n"
    "Connection: close\r\n\r\n"
    "Invalid header received from browser.\r\n";
 
-const static char CFORBIDDEN[] =
+static const char CFORBIDDEN[] =
    "HTTP/1.0 403 Connection not allowable\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "X-Hint: If you read this message interactively, then you know why this happens ,-)\r\n"
    "Connection: close\r\n\r\n";
 
-const static char FTP_RESPONSE[] =
+static const char FTP_RESPONSE[] =
    "HTTP/1.0 400 Invalid request received from browser\r\n"
    "Content-Type: text/plain\r\n"
    "Connection: close\r\n\r\n"
    "Invalid request. Privoxy doesn't support FTP.\r\n";
 
-const static char GOPHER_RESPONSE[] =
+static const char GOPHER_RESPONSE[] =
    "HTTP/1.0 400 Invalid request received from browser\r\n"
    "Content-Type: text/plain\r\n"
    "Connection: close\r\n\r\n"
    "Invalid request. Privoxy doesn't support gopher.\r\n";
 
 /* XXX: should be a template */
-const static char MISSING_DESTINATION_RESPONSE[] =
+static const char MISSING_DESTINATION_RESPONSE[] =
    "HTTP/1.0 400 Bad request received from browser\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "Content-Type: text/plain\r\n"
@@ -1047,7 +1104,7 @@ const static char MISSING_DESTINATION_RESPONSE[] =
    "Bad request. Privoxy was unable to extract the destination.\r\n";
 
 /* XXX: should be a template */
-const static char NO_SERVER_DATA_RESPONSE[] =
+static const char NO_SERVER_DATA_RESPONSE[] =
    "HTTP/1.0 502 Server or forwarder response empty\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "Content-Type: text/plain\r\n"
@@ -1055,16 +1112,18 @@ const static char NO_SERVER_DATA_RESPONSE[] =
    "Empty server or forwarder response.\r\n"
    "The connection was closed without sending any data.\r\n";
 
+#if 0
 /* XXX: should be a template */
-const static char NULL_BYTE_RESPONSE[] =
+static const char NULL_BYTE_RESPONSE[] =
    "HTTP/1.0 400 Bad request received from browser\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "Content-Type: text/plain\r\n"
    "Connection: close\r\n\r\n"
    "Bad request. Null byte(s) before end of request.\r\n";
+#endif
 
 /* XXX: should be a template */
-const static char MESSED_UP_REQUEST_RESPONSE[] =
+static const char MESSED_UP_REQUEST_RESPONSE[] =
    "HTTP/1.0 400 Malformed request after rewriting\r\n"
    "Proxy-Agent: Privoxy " VERSION "\r\n"
    "Content-Type: text/plain\r\n"
@@ -1074,8 +1133,6 @@ const static char MESSED_UP_REQUEST_RESPONSE[] =
 /* A function to crunch a response */
 typedef struct http_response *(*crunch_func_ptr)(struct client_state *);
 
-typedef char *(*filter_function_ptr)();
-
 /* Crunch function flags */
 #define CF_NO_FLAGS        0
 /* Cruncher applies to forced requests as well */
@@ -1090,8 +1147,10 @@ struct cruncher
    const int flags;
 };
 
+static int crunch_response_triggered(struct client_state *csp, const struct cruncher crunchers[]);
+
 /* Complete list of cruncher functions */
-const static struct cruncher crunchers_all[] = {
+static const struct cruncher crunchers_all[] = {
    { direct_response, CF_COUNT_AS_REJECT|CF_IGNORE_FORCE},
    { block_url,       CF_COUNT_AS_REJECT },
 #ifdef FEATURE_TRUST
@@ -1103,7 +1162,7 @@ const static struct cruncher crunchers_all[] = {
 };
 
 /* Light version, used after tags are applied */
-const static struct cruncher crunchers_light[] = {
+static const struct cruncher crunchers_light[] = {
    { block_url,       CF_COUNT_AS_REJECT },
    { redirect_url,    CF_NO_FLAGS },
    { NULL,            0 }
@@ -1175,7 +1234,7 @@ static void sig_handler(int the_signal)
  *                FALSE if the request doesn't look invalid.
  *
  *********************************************************************/
-int client_protocol_is_unsupported(const struct client_state *csp, char *req)
+static int client_protocol_is_unsupported(const struct client_state *csp, char *req)
 {
    char buf[BUFFER_SIZE];
 
@@ -1244,7 +1303,7 @@ int client_protocol_is_unsupported(const struct client_state *csp, char *req)
  *                JB_ERR_PARSE if it isn't.
  *
  *********************************************************************/
-jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers)
+static jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *headers)
 {
    char *req;
 
@@ -1311,7 +1370,7 @@ jb_err get_request_destination_elsewhere(struct client_state *csp, struct list *
  *                JB_ERR_PARSE if the headers were incomplete.
  *
  *********************************************************************/
-jb_err get_server_headers(struct client_state *csp)
+static jb_err get_server_headers(struct client_state *csp)
 {
    int continue_hack_in_da_house = 0;
    char * header;
@@ -1397,7 +1456,7 @@ jb_err get_server_headers(struct client_state *csp)
  * Returns     :  A string with the crunch reason or an error description.
  *
  *********************************************************************/
-const char *crunch_reason(const struct http_response *rsp)
+static const char *crunch_reason(const struct http_response *rsp)
 {
    char * reason = NULL;
 
@@ -1460,7 +1519,7 @@ const char *crunch_reason(const struct http_response *rsp)
  * Returns     :  Nothing.
  *
  *********************************************************************/
-void send_crunch_response(struct client_state *csp, struct http_response *rsp)
+static void send_crunch_response(struct client_state *csp, struct http_response *rsp)
 {
       const struct http_request *http = csp->http;
       char status_code[4];
@@ -1523,6 +1582,7 @@ void send_crunch_response(struct client_state *csp, struct http_response *rsp)
 }
 
 
+#if 0
 /*********************************************************************
  *
  * Function    :  request_contains_null_bytes
@@ -1530,6 +1590,8 @@ void send_crunch_response(struct client_state *csp, struct http_response *rsp)
  * Description :  Checks for NULL bytes in the request and sends
  *                an error message to the client if any were found.
  *
+ *                XXX: currently not used, see comment in chat().
+ *
  * Parameters  :
  *          1  :  csp = Current client state (buffers, headers, etc...)
  *          2  :  buf = Data from the client's request to check.
@@ -1539,7 +1601,7 @@ void send_crunch_response(struct client_state *csp, struct http_response *rsp)
  *                FALSE otherwise.
  *
  *********************************************************************/
-int request_contains_null_bytes(const struct client_state *csp, char *buf, int len)
+static int request_contains_null_bytes(const struct client_state *csp, char *buf, int len)
 {
    size_t c_len; /* Request lenght when treated as C string */
 
@@ -1579,6 +1641,7 @@ int request_contains_null_bytes(const struct client_state *csp, char *buf, int l
 
    return FALSE;
 }
+#endif
 
 
 /*********************************************************************
@@ -1596,7 +1659,7 @@ int request_contains_null_bytes(const struct client_state *csp, char *buf, int l
  *                FALSE otherwise.
  *
  *********************************************************************/
-int crunch_response_triggered(struct client_state *csp, const struct cruncher crunchers[])
+static int crunch_response_triggered(struct client_state *csp, const struct cruncher crunchers[])
 {
    struct http_response *rsp = NULL;
    const struct cruncher *c;
@@ -1664,7 +1727,7 @@ int crunch_response_triggered(struct client_state *csp, const struct cruncher cr
  * Returns     :  Nothing. Terminates in case of memory problems.
  *
  *********************************************************************/
-void build_request_line(struct client_state *csp, const struct forward_spec *fwd, char **request_line)
+static void build_request_line(struct client_state *csp, const struct forward_spec *fwd, char **request_line)
 {
    struct http_request *http = csp->http;
 
@@ -1726,7 +1789,7 @@ void build_request_line(struct client_state *csp, const struct forward_spec *fwd
  *                Terminates in case of memory problems.
  *
  *********************************************************************/
-jb_err change_request_destination(struct client_state *csp)
+static jb_err change_request_destination(struct client_state *csp)
 {
    struct http_request *http = csp->http;
    jb_err err;
@@ -1749,87 +1812,6 @@ jb_err change_request_destination(struct client_state *csp)
 }
 
 
-/*********************************************************************
- *
- * Function    :  get_filter_function
- *
- * Description :  Decides which content filter function has
- *                to be applied (if any).
- *
- *                XXX: Doesn't handle filter_popups()
- *                because of the different prototype. Probably
- *                we should ditch filter_popups() anyway, it's
- *                even less reliable than popup blocking based
- *                on pcrs filters.
- *
- * Parameters  :
- *          1  :  csp = Current client state (buffers, headers, etc...)
- *
- * Returns     :  The content filter function to run, or
- *                NULL if no content filter is active
- *
- *********************************************************************/
-filter_function_ptr get_filter_function(struct client_state *csp)
-{
-   filter_function_ptr filter_function = NULL;
-
-   /*
-    * Are we enabling text mode by force?
-    */
-   if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
-   {
-      /*
-       * Do we really have to?
-       */
-      if (csp->content_type & CT_TEXT)
-      {
-         log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");   
-      }
-      else
-      {
-         csp->content_type |= CT_TEXT;
-         log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");   
-      }
-   }
-
-   if (!(csp->content_type & CT_DECLARED))
-   {
-      /*
-       * The server didn't bother to declare a MIME-Type.
-       * Assume it's text that can be filtered.
-       *
-       * This also regulary happens with 304 responses,
-       * therefore logging anything here would cause
-       * too much noise.
-       */
-      csp->content_type |= CT_TEXT;
-   }
-
-
-   /*
-    * Choose the applying filter function based on
-    * the content type and action settings.
-    */
-   if ((csp->content_type & CT_TEXT) &&
-       (csp->rlist != NULL) &&
-       (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
-   {
-      filter_function = pcrs_filter_response;
-   }
-   else if ((csp->content_type & CT_GIF)  &&
-            (csp->action->flags & ACTION_DEANIMATE))
-   {
-      filter_function = gif_deanimate_response;
-   }
-   else if ((csp->content_type & CT_JPEG)  &&
-            (csp->action->flags & ACTION_JPEG_INSPECT))
-   {
-      filter_function = jpeg_inspect_response;
-   }
-
-   return filter_function;
-}
-
 /*********************************************************************
  *
  * Function    :  chat
@@ -1854,7 +1836,7 @@ static void chat(struct client_state *csp)
    char buf[BUFFER_SIZE];
    char *hdr;
    char *p;
-   char *req;
+   char *req = NULL;
    fd_set rfds;
    int n;
    jb_socket maxfd;
@@ -1909,7 +1891,7 @@ static void chat(struct client_state *csp)
 
    } while ((NULL != req) && ('\0' == *req));
 
-   if (NULL != req)
+   if ((NULL != req) && ('\0' != *req))
    {
       /* Request received. Validate and parse it. */
 
@@ -2072,16 +2054,6 @@ static void chat(struct client_state *csp)
    list_append_list_unique(csp->headers, headers);
    destroy_list(headers);
 
-   /*
-    * If the user has not supplied any wafers, and the user has not
-    * told us to suppress the vanilla wafer, then send the vanilla wafer.
-    */
-   if (list_is_empty(csp->action->multi[ACTION_MULTI_WAFER])
-       && ((csp->action->flags & ACTION_VANILLA_WAFER) != 0))
-   {
-      enlist(csp->action->multi[ACTION_MULTI_WAFER], VANILLA_WAFER);
-   }
-
    err = sed(client_patterns, add_client_headers, csp);
    if (JB_ERR_OK != err)
    {
@@ -2090,22 +2062,21 @@ static void chat(struct client_state *csp)
    }
    csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE;
 
-   if (strcmp(http->cmd, csp->headers->first->str))
+   /* Check request line for rewrites. */
+   if ((NULL == csp->headers->first->str)
+      || (strcmp(http->cmd, csp->headers->first->str) &&
+         (JB_ERR_OK != change_request_destination(csp))))
    {
       /*
-       * A header filter rewrote the request line,
-       * modify the http request accordingly.
+       * A header filter broke the request line - bail out.
        */
-      if (JB_ERR_OK != change_request_destination(csp))
-      {
-         write_socket(csp->cfd, MESSED_UP_REQUEST_RESPONSE, strlen(MESSED_UP_REQUEST_RESPONSE));
-         /* XXX: Use correct size */
-         log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request generated\" 500 0", csp->ip_addr_str);
-         log_error(LOG_LEVEL_ERROR, "Invalid request line after applying header filters.");
+      write_socket(csp->cfd, MESSED_UP_REQUEST_RESPONSE, strlen(MESSED_UP_REQUEST_RESPONSE));
+      /* XXX: Use correct size */
+      log_error(LOG_LEVEL_CLF, "%s - - [%T] \"Invalid request generated\" 500 0", csp->ip_addr_str);
+      log_error(LOG_LEVEL_ERROR, "Invalid request line after applying header filters.");
 
-         free_http_request(http);
-         return;
-      }
+      free_http_request(http);
+      return;
    }
 
    /* decide how to route the HTTP request */
@@ -2115,7 +2086,8 @@ static void chat(struct client_state *csp)
       /* Never get here - LOG_LEVEL_FATAL causes program exit */
    }
 
-   /* build the http request to send to the server
+   /*
+    * build the http request to send to the server
     * we have to do one of the following:
     *
     * create = use the original HTTP request to create a new
@@ -2406,10 +2378,23 @@ static void chat(struct client_state *csp)
                   "CONNECT already confirmed. Unable to tell the client about the problem.");
                return;
             }
+            else if (byte_count)
+            {
+               /*
+                * Just hang up. We already transmitted the original headers
+                * and parts of the original content and therefore missed the
+                * chance to send an error message (without risking data corruption).
+                *
+                * XXX: we could retry with a fancy range request here.
+                */
+               log_error(LOG_LEVEL_ERROR, "Already forwarded the original headers. "
+                  "Unable to tell the client about the problem.");
+               return;
+            }
 
             rsp = error_response(csp, "connect-failed", errno);
 
-            if(rsp)
+            if (rsp)
             {
                send_crunch_response(csp, rsp);
             }
@@ -2460,12 +2445,13 @@ static void chat(struct client_state *csp)
                 */
                if (content_filter)
                {
+                  p = execute_content_filter(csp, content_filter);
                   /*
                    * If the content filter fails, use the original
                    * buffer and length.
                    * (see p != NULL ? p : csp->iob->cur below)
                    */
-                  if (NULL == (p = (*content_filter)(csp)))
+                  if (NULL == p)
                   {
                      csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur);
                   }
@@ -2540,10 +2526,7 @@ static void chat(struct client_state *csp)
                   int flushed;
 
                   log_error(LOG_LEVEL_ERROR, "Flushing header and buffers. Stepping back from filtering.");
-                  if (JB_ERR_OK != sed(server_patterns, add_server_headers, csp))
-                  {
-                     log_error(LOG_LEVEL_FATAL, "Failed to parse server headers.");
-                  }
+
                   hdr = list_to_text(csp->headers);
                   if (hdr == NULL)
                   {
@@ -2557,19 +2540,6 @@ static void chat(struct client_state *csp)
 
                      return;
                   }
-
-                  if (crunch_response_triggered(csp, crunchers_light))
-                  {
-                     /*
-                      * One of the tags created by a server-header
-                      * tagger triggered a crunch. We already
-                      * delivered the crunch response to the client
-                      * and are done here after cleaning up.
-                      */
-                     freez(hdr);
-                     return;
-                  }
-
                   hdrlen = strlen(hdr);
 
                   if (write_socket(csp->cfd, hdr, hdrlen)
@@ -2582,11 +2552,15 @@ static void chat(struct client_state *csp)
                      return;
                   }
 
-                  byte_count += hdrlen + (size_t)flushed + (size_t)len;
+                  /*
+                   * Reset the byte_count to the amount of bytes
+                   * we just flushed. len will be added a few lines below,
+                   * hdrlen doesn't matter for LOG_LEVEL_CLF.
+                   */
+                  byte_count = (size_t)flushed;
                   freez(hdr);
                   content_filter = NULL;
                   server_body = 1;
-
                }
             }
             else
@@ -2816,6 +2790,7 @@ static int32 server_thread(void *data)
 #endif
 
 
+#if defined(unix)
 /*********************************************************************
  *
  * Function    :  usage
@@ -2827,7 +2802,7 @@ static int32 server_thread(void *data)
  * Returns     :  No. ,-)
  *
  *********************************************************************/
-void usage(const char *myname)
+static void usage(const char *myname)
 {
    printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"
           "Usage: %s "
@@ -2836,7 +2811,7 @@ void usage(const char *myname)
 #endif /* defined(unix) */
           "[--help] "
 #if defined(unix)
-          "[--no-daemon] [--pidfile pidfile] [--user user[.group]] "
+          "[--no-daemon] [--pidfile pidfile] [--pre-chroot-nslookup hostname] [--user user[.group]] "
 #endif /* defined(unix) */
           "[--version] [configfile]\n"
           "Aborting\n", myname);
@@ -2844,6 +2819,7 @@ void usage(const char *myname)
    exit(2);
 
 }
+#endif /* defined(unix) */
 
 
 /*********************************************************************
@@ -2857,7 +2833,7 @@ void usage(const char *myname)
  * Returns     :  Void, exits in case of errors.
  *
  *********************************************************************/
-void initialize_mutexes()
+static void initialize_mutexes(void)
 {
    int err = 0;
 
@@ -2948,6 +2924,7 @@ int main(int argc, const char *argv[])
    struct group *grp = NULL;
    char *p;
    int do_chroot = 0;
+   char *pre_chroot_nslookup_to_load_resolver = NULL;
 #endif
 
    Argc = argc;
@@ -3042,6 +3019,12 @@ int main(int argc, const char *argv[])
          if (p != NULL) *--p = '\0';
       }
 
+      else if (strcmp(argv[argc_pos], "--pre-chroot-nslookup" ) == 0)
+      {
+         if (++argc_pos == argc) usage(argv[0]);
+         pre_chroot_nslookup_to_load_resolver = strdup(argv[argc_pos]);
+      }
+
       else if (strcmp(argv[argc_pos], "--chroot" ) == 0)
       {
          do_chroot = 1;
@@ -3250,6 +3233,14 @@ int main(int argc, const char *argv[])
          {
             log_error(LOG_LEVEL_FATAL, "Home directory for %s undefined", pw->pw_name);
          }
+         /* Read the time zone file from /etc before doing chroot. */
+         tzset();
+         if (NULL != pre_chroot_nslookup_to_load_resolver
+             && '\0' != pre_chroot_nslookup_to_load_resolver[0])
+         {
+            /* Initialize resolver library. */
+            (void) resolve_hostname_to_ip(pre_chroot_nslookup_to_load_resolver);
+         }
          if (chroot(pw->pw_dir) < 0)
          {
             log_error(LOG_LEVEL_FATAL, "Cannot chroot to %s", pw->pw_dir);