flatpak_run_get_cups_server_name (void)
{
  g_autofree char * cups_server = NULL;
  g_autofree char * cups_config_path = NULL;

  /* TODO
   * we don't currently support cups servers located on the network, if such
   * server is detected, we simply ignore it and in the worst case we fallback
   * to the default socket
   */
  cups_server = g_strdup (g_getenv ("CUPS_SERVER"));
  if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server))
    return g_steal_pointer (&cups_server);
  g_clear_pointer (&cups_server, g_free);

  cups_config_path = g_build_filename (g_get_home_dir (), ".cups/client.conf", NULL);
  cups_server = flatpak_run_get_cups_server_name_config (cups_config_path);
  if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server))
    return g_steal_pointer (&cups_server);
  g_clear_pointer (&cups_server, g_free);

  cups_server = flatpak_run_get_cups_server_name_config ("/etc/cups/client.conf");
  if (cups_server && flatpak_run_cups_check_server_is_socket (cups_server))
    return g_steal_pointer (&cups_server);

  // Fallback to default socket
  return g_strdup ("/var/run/cups/cups.sock");
}


flatpak_run_cups_check_server_is_socket (const char *server)
{
  if (g_str_has_prefix (server, "/") && strstr (server, ":") == NULL)
    return TRUE;

  return FALSE;
}

run: Add cross-references for some other seccomp syscall filters

Signed-off-by: Simon McVittie <smcv@collabora.com>


write_xauth (char *number, FILE *output)
{
  Xauth *xa, local_xa;
  char *filename;
  FILE *f;
  struct utsname unames;

  if (uname (&unames))
    {
      g_warning ("uname failed");
      return;
    }

  filename = XauFileName ();
  f = fopen (filename, "rb");
  if (f == NULL)
    return;

  while (TRUE)
    {
      xa = XauReadAuth (f);
      if (xa == NULL)
        break;
      if (xauth_entry_should_propagate (xa, unames.nodename, number))
        {
          local_xa = *xa;
          if (local_xa.number)
            {
              local_xa.number = "99";
              local_xa.number_length = 2;
            }

          if (local_xa.family == FamilyLocal &&
              !auth_streq (unames.nodename, local_xa.address, local_xa.address_length))
            {
              /* If we decided to propagate this cookie, but its address
               * doesn't match `unames.nodename`, we need to change it or
               * inside the container it will not work.
               */
              local_xa.address = unames.nodename;
              local_xa.address_length = strlen (local_xa.address);
            }

          if (!XauWriteAuth (output, &local_xa))
            g_warning ("xauth write error");
        }

      XauDisposeAuth (xa);
    }

  fclose (f);
}


flatpak_run_add_environment_args (FlatpakBwrap    *bwrap,
                                  const char      *app_info_path,
                                  FlatpakRunFlags  flags,
                                  const char      *app_id,
                                  FlatpakContext  *context,
                                  GFile           *app_id_dir,
                                  GPtrArray       *previous_app_id_dirs,
                                  int              per_app_dir_lock_fd,
                                  FlatpakExports **exports_out,
                                  GCancellable    *cancellable,
                                  GError         **error)
{
  g_autoptr(GError) my_error = NULL;
  g_autoptr(FlatpakExports) exports = NULL;
  g_autoptr(FlatpakBwrap) proxy_arg_bwrap = flatpak_bwrap_new (flatpak_bwrap_empty_env);
  g_autofree char *xdg_dirs_conf = NULL;
  gboolean has_wayland = FALSE;
  gboolean allow_x11 = FALSE;
  gboolean home_access = FALSE;
  gboolean sandboxed = (flags & FLATPAK_RUN_FLAG_SANDBOX) != 0;

  if ((context->shares & FLATPAK_CONTEXT_SHARED_IPC) == 0)
    {
      g_debug ("Disallowing ipc access");
      flatpak_bwrap_add_args (bwrap, "--unshare-ipc", NULL);
    }

  if ((context->shares & FLATPAK_CONTEXT_SHARED_NETWORK) == 0)
    {
      g_debug ("Disallowing network access");
      flatpak_bwrap_add_args (bwrap, "--unshare-net", NULL);
    }

  if (context->devices & FLATPAK_CONTEXT_DEVICE_ALL)
    {
      flatpak_bwrap_add_args (bwrap,
                              "--dev-bind", "/dev", "/dev",
                              NULL);
      /* Don't expose the host /dev/shm, just the device nodes, unless explicitly allowed */
      if (g_file_test ("/dev/shm", G_FILE_TEST_IS_DIR))
        {
          if (context->devices & FLATPAK_CONTEXT_DEVICE_SHM)
            {
              /* Don't do anything special: include shm in the
               * shared /dev. The host and all sandboxes and subsandboxes
               * all share /dev/shm */
            }
          else if ((context->features & FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM)
                   && per_app_dir_lock_fd >= 0)
            {
              g_autofree char *shared_dev_shm = NULL;

              /* The host and the original sandbox have separate /dev/shm,
               * but we want other instances to be able to share /dev/shm with
               * the first sandbox (except for subsandboxes run with
               * flatpak-spawn --sandbox, which will have their own). */
              if (!flatpak_instance_ensure_per_app_dev_shm (app_id,
                                                            per_app_dir_lock_fd,
                                                            &shared_dev_shm,
                                                            error))
                return FALSE;

              flatpak_bwrap_add_args (bwrap,
                                      "--bind", shared_dev_shm, "/dev/shm",
                                      NULL);
            }
          else
            {
              /* The host, the original sandbox and each subsandbox
               * each have a separate /dev/shm. */
              flatpak_bwrap_add_args (bwrap,
                                      "--tmpfs", "/dev/shm",
                                      NULL);
            }
        }
      else if (g_file_test ("/dev/shm", G_FILE_TEST_IS_SYMLINK))
        {
          g_autofree char *link = flatpak_readlink ("/dev/shm", NULL);

          /* On debian (with sysv init) the host /dev/shm is a symlink to /run/shm, so we can't
             mount on top of it. */
          if (g_strcmp0 (link, "/run/shm") == 0)
            {
              if (context->devices & FLATPAK_CONTEXT_DEVICE_SHM &&
                  g_file_test ("/run/shm", G_FILE_TEST_IS_DIR))
                {
                  flatpak_bwrap_add_args (bwrap,
                                          "--bind", "/run/shm", "/run/shm",
                                          NULL);
                }
              else if ((context->features & FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM)
                       && per_app_dir_lock_fd >= 0)
                {
                  g_autofree char *shared_dev_shm = NULL;

                  /* The host and the original sandbox have separate /dev/shm,
                   * but we want other instances to be able to share /dev/shm,
                   * except for flatpak-spawn --subsandbox. */
                  if (!flatpak_instance_ensure_per_app_dev_shm (app_id,
                                                                per_app_dir_lock_fd,
                                                                &shared_dev_shm,
                                                                error))
                    return FALSE;

                  flatpak_bwrap_add_args (bwrap,
                                          "--bind", shared_dev_shm, "/run/shm",
                                          NULL);
                }
              else
                {
                  flatpak_bwrap_add_args (bwrap,
                                          "--dir", "/run/shm",
                                          NULL);
                }
            }
          else
            g_warning ("Unexpected /dev/shm symlink %s", link);
        }
    }
  else
    {
      flatpak_bwrap_add_args (bwrap,
                              "--dev", "/dev",
                              NULL);
      if (context->devices & FLATPAK_CONTEXT_DEVICE_DRI)
        {
          g_debug ("Allowing dri access");
          int i;
          char *dri_devices[] = {
            "/dev/dri",
            /* mali */
            "/dev/mali",
            "/dev/mali0",
            "/dev/umplock",
            /* nvidia */
            "/dev/nvidiactl",
            "/dev/nvidia-modeset",
            /* nvidia OpenCL/CUDA */
            "/dev/nvidia-uvm",
            "/dev/nvidia-uvm-tools",
          };

          for (i = 0; i < G_N_ELEMENTS (dri_devices); i++)
            {
              if (g_file_test (dri_devices[i], G_FILE_TEST_EXISTS))
                flatpak_bwrap_add_args (bwrap, "--dev-bind", dri_devices[i], dri_devices[i], NULL);
            }

          /* Each Nvidia card gets its own device.
             This is a fairly arbitrary limit but ASUS sells mining boards supporting 20 in theory. */
          char nvidia_dev[14]; /* /dev/nvidia plus up to 2 digits */
          for (i = 0; i < 20; i++)
            {
              g_snprintf (nvidia_dev, sizeof (nvidia_dev), "/dev/nvidia%d", i);
              if (g_file_test (nvidia_dev, G_FILE_TEST_EXISTS))
                flatpak_bwrap_add_args (bwrap, "--dev-bind", nvidia_dev, nvidia_dev, NULL);
            }
        }

      if (context->devices & FLATPAK_CONTEXT_DEVICE_KVM)
        {
          g_debug ("Allowing kvm access");
          if (g_file_test ("/dev/kvm", G_FILE_TEST_EXISTS))
            flatpak_bwrap_add_args (bwrap, "--dev-bind", "/dev/kvm", "/dev/kvm", NULL);
        }

      if (context->devices & FLATPAK_CONTEXT_DEVICE_SHM)
        {
          /* This is a symlink to /run/shm on debian, so bind to real target */
          g_autofree char *real_dev_shm = realpath ("/dev/shm", NULL);

          g_debug ("Allowing /dev/shm access (as %s)", real_dev_shm);
          if (real_dev_shm != NULL)
              flatpak_bwrap_add_args (bwrap, "--bind", real_dev_shm, "/dev/shm", NULL);
        }
      else if ((context->features & FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM)
               && per_app_dir_lock_fd >= 0)
        {
          g_autofree char *shared_dev_shm = NULL;

          if (!flatpak_instance_ensure_per_app_dev_shm (app_id,
                                                        per_app_dir_lock_fd,
                                                        &shared_dev_shm,
                                                        error))
            return FALSE;

          flatpak_bwrap_add_args (bwrap,
                                  "--bind", shared_dev_shm, "/dev/shm",
                                  NULL);
        }
    }

  exports = flatpak_context_get_exports_full (context,
                                              app_id_dir, previous_app_id_dirs,
                                              TRUE, TRUE,
                                              &xdg_dirs_conf, &home_access);

  if (flatpak_exports_path_is_visible (exports, "/tmp"))
    {
      /* The original sandbox and any subsandboxes are both already
       * going to share /tmp with the host, so by transitivity they will
       * also share it with each other, and with all other instances. */
    }
  else if (per_app_dir_lock_fd >= 0 && !sandboxed)
    {
      g_autofree char *shared_tmp = NULL;

      /* The host and the original sandbox have separate /tmp,
       * but we want other instances to be able to share /tmp with the
       * first sandbox, unless they were created by
       * flatpak-spawn --sandbox.
       *
       * In apply_extra and `flatpak build`, per_app_dir_lock_fd is
       * negative and we skip this. */
      if (!flatpak_instance_ensure_per_app_tmp (app_id,
                                                per_app_dir_lock_fd,
                                                &shared_tmp,
                                                error))
        return FALSE;

      flatpak_bwrap_add_args (bwrap,
                              "--bind", shared_tmp, "/tmp",
                              NULL);
    }

  flatpak_context_append_bwrap_filesystem (context, bwrap, app_id, app_id_dir,
                                           exports, xdg_dirs_conf, home_access);

  if (context->sockets & FLATPAK_CONTEXT_SOCKET_WAYLAND)
    {
      g_debug ("Allowing wayland access");
      has_wayland = flatpak_run_add_wayland_args (bwrap);
    }

  if ((context->sockets & FLATPAK_CONTEXT_SOCKET_FALLBACK_X11) != 0)
    allow_x11 = !has_wayland;
  else
    allow_x11 = (context->sockets & FLATPAK_CONTEXT_SOCKET_X11) != 0;

  flatpak_run_add_x11_args (bwrap, allow_x11);

  if (context->sockets & FLATPAK_CONTEXT_SOCKET_SSH_AUTH)
    {
      flatpak_run_add_ssh_args (bwrap);
    }

  if (context->sockets & FLATPAK_CONTEXT_SOCKET_PULSEAUDIO)
    {
      g_debug ("Allowing pulseaudio access");
      flatpak_run_add_pulseaudio_args (bwrap);
    }

  if (context->sockets & FLATPAK_CONTEXT_SOCKET_PCSC)
    {
      flatpak_run_add_pcsc_args (bwrap);
    }

  if (context->sockets & FLATPAK_CONTEXT_SOCKET_CUPS)
    {
      flatpak_run_add_cups_args (bwrap);
    }

  flatpak_run_add_session_dbus_args (bwrap, proxy_arg_bwrap, context, flags, app_id);
  flatpak_run_add_system_dbus_args (bwrap, proxy_arg_bwrap, context, flags);
  flatpak_run_add_a11y_dbus_args (bwrap, proxy_arg_bwrap, context, flags);

  /* Must run this before spawning the dbus proxy, to ensure it
     ends up in the app cgroup */
  if (!flatpak_run_in_transient_unit (app_id, &my_error))
    {
      /* We still run along even if we don't get a cgroup, as nothing
         really depends on it. Its just nice to have */
      g_debug ("Failed to run in transient scope: %s", my_error->message);
      g_clear_error (&my_error);
    }

  if (!flatpak_bwrap_is_empty (proxy_arg_bwrap) &&
      !start_dbus_proxy (bwrap, proxy_arg_bwrap, app_info_path, error))
    return FALSE;

  if (exports_out)
    *exports_out = g_steal_pointer (&exports);

  return TRUE;
}


flatpak_run_get_pulse_runtime_dir (void)
{
  const char *val = NULL;

  val = g_getenv ("PULSE_RUNTIME_PATH");

  if (val != NULL)
    return realpath (val, NULL);

  {
    const char *user_runtime_dir = g_get_user_runtime_dir ();

    if (user_runtime_dir != NULL)
      {
        g_autofree char *dir = g_build_filename (user_runtime_dir, "pulse", NULL);

        if (g_file_test (dir, G_FILE_TEST_IS_DIR))
          return realpath (dir, NULL);
      }
  }

  {
    g_autofree char *pulse_home = flatpak_run_get_pulse_home ();
    g_autofree char *machine_id = flatpak_run_get_pulse_machine_id ();

    if (pulse_home != NULL && machine_id != NULL)
      {
        /* This is usually a symlink, but we take its realpath() anyway */
        g_autofree char *dir = g_strdup_printf ("%s/%s-runtime", pulse_home, machine_id);

        if (g_file_test (dir, G_FILE_TEST_IS_DIR))
          return realpath (dir, NULL);
      }
  }

  return NULL;
}


auth_streq (char *str,
            char *au_str,
            int   au_len)
{
  return au_len == strlen (str) && memcmp (str, au_str, au_len) == 0;
}


flatpak_run_parse_pulse_server (const char *value)
{
  g_auto(GStrv) servers = g_strsplit (value, " ", 0);
  gsize i;

  for (i = 0; servers[i] != NULL; i++)
    {
      const char *server = servers[i];
      if (g_str_has_prefix (server, "{"))
        {
          const char * closing = strstr (server, "}");
          if (closing == NULL)
            continue;
          server = closing + 1;
        }
      if (g_str_has_prefix (server, "unix:"))
        return g_strdup (server + 5);
    }

  return NULL;
}


flatpak_run_add_x11_args (FlatpakBwrap *bwrap,
                          gboolean      allowed)
{
  g_autofree char *x11_socket = NULL;
  const char *display;

  /* Always cover /tmp/.X11-unix, that way we never see the host one in case
   * we have access to the host /tmp. If you request X access we'll put the right
   * thing in this anyway.
   *
   * We need to be a bit careful here, because there are two situations in
   * which potentially hostile processes have access to /tmp and could
   * create symlinks, which in principle could cause us to create the
   * directory and mount the tmpfs at the target of the symlink instead
   * of in the intended place:
   *
   * - With --filesystem=/tmp, it's the host /tmp - but because of the
   *   special historical status of /tmp/.X11-unix, we can assume that
   *   it is pre-created by the host system before user code gets to run.
   *
   * - When /tmp is shared between all instances of the same app ID,
   *   in principle the app has control over what's in /tmp, but in
   *   practice it can't interfere with /tmp/.X11-unix, because we do
   *   this unconditionally - therefore by the time app code runs,
   *   /tmp/.X11-unix is already a mount point, meaning the app cannot
   *   rename or delete it.
   */
  flatpak_bwrap_add_args (bwrap,
                          "--tmpfs", "/tmp/.X11-unix",
                          NULL);

  if (!allowed)
    {
      flatpak_bwrap_unset_env (bwrap, "DISPLAY");
      return;
    }

  g_debug ("Allowing x11 access");

  display = g_getenv ("DISPLAY");
  if (display && display[0] == ':' && g_ascii_isdigit (display[1]))
    {
      const char *display_nr = &display[1];
      const char *display_nr_end = display_nr;
      g_autofree char *d = NULL;

      while (g_ascii_isdigit (*display_nr_end))
        display_nr_end++;

      d = g_strndup (display_nr, display_nr_end - display_nr);
      x11_socket = g_strdup_printf ("/tmp/.X11-unix/X%s", d);

      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", x11_socket, "/tmp/.X11-unix/X99",
                              NULL);
      flatpak_bwrap_set_env (bwrap, "DISPLAY", ":99.0", TRUE);

#ifdef ENABLE_XAUTH
      g_auto(GLnxTmpfile) xauth_tmpf  = { 0, };

      if (glnx_open_anonymous_tmpfile_full (O_RDWR | O_CLOEXEC, "/tmp", &xauth_tmpf, NULL))
        {
          FILE *output = fdopen (xauth_tmpf.fd, "wb");
          if (output != NULL)
            {
              /* fd is now owned by output, steal it from the tmpfile */
              int tmp_fd = dup (glnx_steal_fd (&xauth_tmpf.fd));
              if (tmp_fd != -1)
                {
                  static const char dest[] = "/run/flatpak/Xauthority";

                  write_xauth (d, output);
                  flatpak_bwrap_add_args_data_fd (bwrap, "--ro-bind-data", tmp_fd, dest);

                  flatpak_bwrap_set_env (bwrap, "XAUTHORITY", dest, TRUE);
                }

              fclose (output);

              if (tmp_fd != -1)
                lseek (tmp_fd, 0, SEEK_SET);
            }
        }
#endif
    }
  else
    {
      flatpak_bwrap_unset_env (bwrap, "DISPLAY");
    }
}


cleanup_seccomp (void *p)
{
  scmp_filter_ctx *pp = (scmp_filter_ctx *) p;

  if (*pp)
    seccomp_release (*pp);
}


flatpak_ensure_data_dir (GFile        *app_id_dir,
                         GCancellable *cancellable,
                         GError      **error)
{
  g_autoptr(GFile) data_dir = g_file_get_child (app_id_dir, "data");
  g_autoptr(GFile) cache_dir = g_file_get_child (app_id_dir, "cache");
  g_autoptr(GFile) fontconfig_cache_dir = g_file_get_child (cache_dir, "fontconfig");
  g_autoptr(GFile) tmp_dir = g_file_get_child (cache_dir, "tmp");
  g_autoptr(GFile) config_dir = g_file_get_child (app_id_dir, "config");

  if (!flatpak_mkdir_p (data_dir, cancellable, error))
    return FALSE;

  if (!flatpak_mkdir_p (cache_dir, cancellable, error))
    return FALSE;

  if (!flatpak_mkdir_p (fontconfig_cache_dir, cancellable, error))
    return FALSE;

  if (!flatpak_mkdir_p (tmp_dir, cancellable, error))
    return FALSE;

  if (!flatpak_mkdir_p (config_dir, cancellable, error))
    return FALSE;

  return TRUE;
}


add_dconf_locks_to_list (GString     *s,
                         DConfClient *client,
                         const char  *dir)
{
  g_auto(GStrv) locks = NULL;
  int i;

  locks = dconf_client_list_locks (client, dir, NULL);
  for (i = 0; locks[i]; i++)
    {
      g_string_append (s, locks[i]);
      g_string_append_c (s, '\n');
    }
}


open_namespace_fd_if_needed (const char *path,
                             const char *other_path) {
  struct stat s, other_s;

  if (stat (path, &s) != 0)
    return -1; /* No such namespace, ignore */

  if (stat (other_path, &other_s) != 0)
    return -1; /* No such namespace, ignore */

  /* setns calls fail if the process is already in the desired namespace, hence the
     check here to ensure the namespaces are different. */
  if (s.st_ino != other_s.st_ino)
    return open (path, O_RDONLY|O_CLOEXEC);

  return -1;
}


get_dconf_data (const char  *app_id,
                const char **paths,
                const char  *migrate_path,
                char       **defaults,
                gsize       *defaults_size,
                char       **values,
                gsize       *values_size,
                char       **locks,
                gsize       *locks_size)
{
#ifdef HAVE_DCONF
  DConfClient *client = NULL;
  g_autofree char *prefix = NULL;
#endif
  g_autoptr(GKeyFile) defaults_data = NULL;
  g_autoptr(GKeyFile) values_data = NULL;
  g_autoptr(GString) locks_data = NULL;

  defaults_data = g_key_file_new ();
  values_data = g_key_file_new ();
  locks_data = g_string_new ("");

#ifdef HAVE_DCONF

  client = dconf_client_new ();

  prefix = flatpak_dconf_path_for_app_id (app_id);

  if (migrate_path)
    {
      g_debug ("Add values in dir '%s', prefix is '%s'", migrate_path, prefix);
      if (flatpak_dconf_path_is_similar (migrate_path, prefix))
        add_dconf_dir_to_keyfile (values_data, client, migrate_path, DCONF_READ_USER_VALUE);
      else
        g_warning ("Ignoring D-Conf migrate-path setting %s", migrate_path);
    }

  g_debug ("Add defaults in dir %s", prefix);
  add_dconf_dir_to_keyfile (defaults_data, client, prefix, DCONF_READ_DEFAULT_VALUE);

  g_debug ("Add locks in dir %s", prefix);
  add_dconf_locks_to_list (locks_data, client, prefix);

  /* We allow extra paths for defaults and locks, but not for user values */
  if (paths)
    {
      int i;
      for (i = 0; paths[i]; i++)
        {
          if (dconf_is_dir (paths[i], NULL))
            {
              g_debug ("Add defaults in dir %s", paths[i]);
              add_dconf_dir_to_keyfile (defaults_data, client, paths[i], DCONF_READ_DEFAULT_VALUE);

              g_debug ("Add locks in dir %s", paths[i]);
              add_dconf_locks_to_list (locks_data, client, paths[i]);
            }
          else if (dconf_is_key (paths[i], NULL))
            {
              g_debug ("Add individual key %s", paths[i]);
              add_dconf_key_to_keyfile (defaults_data, client, paths[i], DCONF_READ_DEFAULT_VALUE);
              add_dconf_key_to_keyfile (values_data, client, paths[i], DCONF_READ_USER_VALUE);
            }
          else
            {
              g_warning ("Ignoring settings path '%s': neither dir nor key", paths[i]);
            }
        }
    }
#endif

  *defaults = g_key_file_to_data (defaults_data, defaults_size, NULL);
  *values = g_key_file_to_data (values_data, values_size, NULL);
  *locks_size = locks_data->len;
  *locks = g_string_free (g_steal_pointer (&locks_data), FALSE);

#ifdef HAVE_DCONF
  g_object_unref (client);
#endif
}


calculate_ld_cache_checksum (GBytes   *app_deploy_data,
                             GBytes   *runtime_deploy_data,
                             const char *app_extensions,
                             const char *runtime_extensions)
{
  g_autoptr(GChecksum) ld_so_checksum = g_checksum_new (G_CHECKSUM_SHA256);
  if (app_deploy_data)
    g_checksum_update (ld_so_checksum, (guchar *) flatpak_deploy_data_get_commit (app_deploy_data), -1);
  g_checksum_update (ld_so_checksum, (guchar *) flatpak_deploy_data_get_commit (runtime_deploy_data), -1);
  if (app_extensions)
    g_checksum_update (ld_so_checksum, (guchar *) app_extensions, -1);
  if (runtime_extensions)
    g_checksum_update (ld_so_checksum, (guchar *) runtime_extensions, -1);

  return g_strdup (g_checksum_get_string (ld_so_checksum));
}


add_font_path_args (FlatpakBwrap *bwrap)
{
  g_autoptr(GString) xml_snippet = g_string_new ("");
  gchar *path_build_tmp = NULL;
  g_autoptr(GFile) user_font1 = NULL;
  g_autoptr(GFile) user_font2 = NULL;
  g_autoptr(GFile) user_font_cache = NULL;
  g_auto(GStrv) system_cache_dirs = NULL;
  gboolean found_cache = FALSE;
  int i;

  g_string_append (xml_snippet,
                   "<?xml version=\"1.0\"?>\n"
                   "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n"
                   "<fontconfig>\n");

  if (g_file_test (SYSTEM_FONTS_DIR, G_FILE_TEST_EXISTS))
    {
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", SYSTEM_FONTS_DIR, "/run/host/fonts",
                              NULL);
      g_string_append_printf (xml_snippet,
                              "\t<remap-dir as-path=\"%s\">/run/host/fonts</remap-dir>\n",
                              SYSTEM_FONTS_DIR);
    }

  if (g_file_test ("/usr/local/share/fonts", G_FILE_TEST_EXISTS))
    {
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", "/usr/local/share/fonts", "/run/host/local-fonts",
                              NULL);
      g_string_append_printf (xml_snippet,
                              "\t<remap-dir as-path=\"%s\">/run/host/local-fonts</remap-dir>\n",
                              "/usr/local/share/fonts");
    }

  system_cache_dirs = g_strsplit (SYSTEM_FONT_CACHE_DIRS, ":", 0);
  for (i = 0; system_cache_dirs[i] != NULL; i++)
    {
      if (g_file_test (system_cache_dirs[i], G_FILE_TEST_EXISTS))
        {
          flatpak_bwrap_add_args (bwrap,
                                  "--ro-bind", system_cache_dirs[i], "/run/host/fonts-cache",
                                  NULL);
          found_cache = TRUE;
          break;
        }
    }

  if (!found_cache)
    {
      /* We ensure these directories are never writable, or fontconfig
         will use them to write the default cache */
      flatpak_bwrap_add_args (bwrap,
                              "--tmpfs", "/run/host/fonts-cache",
                              "--remount-ro", "/run/host/fonts-cache",
                              NULL);
    }

  path_build_tmp = g_build_filename (g_get_user_data_dir (), "fonts", NULL);
  user_font1 = g_file_new_for_path (path_build_tmp);
  g_clear_pointer (&path_build_tmp, g_free);

  path_build_tmp = g_build_filename (g_get_home_dir (), ".fonts", NULL);
  user_font2 = g_file_new_for_path (path_build_tmp);
  g_clear_pointer (&path_build_tmp, g_free);

  if (g_file_query_exists (user_font1, NULL))
    {
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", flatpak_file_get_path_cached (user_font1), "/run/host/user-fonts",
                              NULL);
      g_string_append_printf (xml_snippet,
                              "\t<remap-dir as-path=\"%s\">/run/host/user-fonts</remap-dir>\n",
                              flatpak_file_get_path_cached (user_font1));
    }
  else if (g_file_query_exists (user_font2, NULL))
    {
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", flatpak_file_get_path_cached (user_font2), "/run/host/user-fonts",
                              NULL);
      g_string_append_printf (xml_snippet,
                              "\t<remap-dir as-path=\"%s\">/run/host/user-fonts</remap-dir>\n",
                              flatpak_file_get_path_cached (user_font2));
    }

  path_build_tmp = g_build_filename (g_get_user_cache_dir (), "fontconfig", NULL);
  user_font_cache = g_file_new_for_path (path_build_tmp);
  g_clear_pointer (&path_build_tmp, g_free);

  if (g_file_query_exists (user_font_cache, NULL))
    {
      flatpak_bwrap_add_args (bwrap,
                              "--ro-bind", flatpak_file_get_path_cached (user_font_cache), "/run/host/user-fonts-cache",
                              NULL);
    }
  else
    {
      /* We ensure these directories are never writable, or fontconfig
         will use them to write the default cache */
      flatpak_bwrap_add_args (bwrap,
                              "--tmpfs", "/run/host/user-fonts-cache",
                              "--remount-ro", "/run/host/user-fonts-cache",
                              NULL);
    }

  g_string_append (xml_snippet,
                   "</fontconfig>\n");

  if (!flatpak_bwrap_add_args_data (bwrap, "font-dirs.xml", xml_snippet->str, xml_snippet->len, "/run/host/font-dirs.xml", NULL))
    g_warning ("Unable to add fontconfig data snippet");
}


add_bwrap_wrapper (FlatpakBwrap *bwrap,
                   const char   *app_info_path,
                   GError      **error)
{
  glnx_autofd int app_info_fd = -1;
  g_auto(GLnxDirFdIterator) dir_iter = { 0 };
  struct dirent *dent;
  g_autofree char *user_runtime_dir = flatpak_get_real_xdg_runtime_dir ();
  g_autofree char *proxy_socket_dir = g_build_filename (user_runtime_dir, ".dbus-proxy/", NULL);

  app_info_fd = open (app_info_path, O_RDONLY | O_CLOEXEC);
  if (app_info_fd == -1)
    return glnx_throw_errno_prefix (error, _("Failed to open app info file"));

  if (!glnx_dirfd_iterator_init_at (AT_FDCWD, "/", FALSE, &dir_iter, error))
    return FALSE;

  flatpak_bwrap_add_arg (bwrap, flatpak_get_bwrap ());

  while (TRUE)
    {
      glnx_autofd int o_path_fd = -1;
      struct statfs stfs;

      if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dir_iter, &dent, NULL, error))
        return FALSE;

      if (dent == NULL)
        break;

      if (strcmp (dent->d_name, ".flatpak-info") == 0)
        continue;

      /* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */
      o_path_fd = openat (dir_iter.fd, dent->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
      if (o_path_fd == -1 || fstatfs (o_path_fd, &stfs) != 0 || stfs.f_type == AUTOFS_SUPER_MAGIC)
        continue; /* AUTOFS mounts are risky and can cause us to block (see issue #1633), so ignore it. Its unlikely the proxy needs such a directory. */

      if (dent->d_type == DT_DIR)
        {
          if (strcmp (dent->d_name, "tmp") == 0 ||
              strcmp (dent->d_name, "var") == 0 ||
              strcmp (dent->d_name, "run") == 0)
            flatpak_bwrap_add_arg (bwrap, "--bind");
          else
            flatpak_bwrap_add_arg (bwrap, "--ro-bind");

          flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name);
          flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name);
        }
      else if (dent->d_type == DT_LNK)
        {
          g_autofree gchar *target = NULL;

          target = glnx_readlinkat_malloc (dir_iter.fd, dent->d_name,
                                           NULL, error);
          if (target == NULL)
            return FALSE;
          flatpak_bwrap_add_args (bwrap, "--symlink", target, NULL);
          flatpak_bwrap_add_arg_printf (bwrap, "/%s", dent->d_name);
        }
    }

  flatpak_bwrap_add_args (bwrap, "--bind", proxy_socket_dir, proxy_socket_dir, NULL);

  /* This is a file rather than a bind mount, because it will then
     not be unmounted from the namespace when the namespace dies. */
  flatpak_bwrap_add_args_data_fd (bwrap, "--file", glnx_steal_fd (&app_info_fd), "/.flatpak-info");

  if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
    return FALSE;

  return TRUE;
}


explicit BroadcastOpLowering(MLIRContext* ctx) : OpRewritePattern(ctx) {}

Avoid Segfault for scalar shapes.

Calling tensor::FromElementsOp with an empty vector of elements and no type
causes a segfault. We need to let the FromElementsOp know which scalar type it
should have.
Also add back the DynamicBroadcastInDimOp canonicalization patterns, which
previously prevented this bug from happening.
Add a regression test that demonstrates the bug.

PiperOrigin-RevId: 417561444
Change-Id: I6d1d6cfb71aabbad6102422625a00bbe253ac95a


DynamicBroadcastInDimOpLowering::DynamicBroadcastInDimOpLowering(
    MLIRContext* ctx)
    : Base(ctx) {}


llvm::Optional<Value> simplifyBroadcast(ShapeComponentAnalysis& analysis,
                                        ValueRange shapes, Location loc,
                                        OpBuilder* builder) {
  // First find the input shape with the largest rank.
  SmallVector<ArrayRef<ShapeComponentAnalysis::SymbolicExpr>> shapes_found;
  size_t maxRank = 0;
  for (const auto &shape : llvm::enumerate(shapes)) {
    auto found_shape = analysis.GetValueInfo(shape.value());
    if (!found_shape) return {};
    shapes_found.push_back(*found_shape);
    maxRank = std::max(maxRank, found_shape->size());
  }
  if (maxRank == 0) {
    return Value(builder->create<tensor::FromElementsOp>(
        loc, shapes[0].getType(), SmallVector<Value>()));
  }

  SmallVector<const ShapeComponentAnalysis::SymbolicExpr*> joined_dimensions(
      maxRank);
  SmallVector<std::pair<Value, int64_t>> shape_and_rank_for_dim(maxRank);
  for (const auto &shape : llvm::enumerate(shapes_found)) {
    for (const auto &dim : llvm::enumerate(llvm::reverse(shape.value()))) {
      // 1 dimensions don't contribute to the final result.
      if (dim.value().isConstant(1)) continue;
      // If it's not a 1 dimension it will be present in the result. Remember
      // where it came from.
      auto index = maxRank - dim.index() - 1;
      if (!joined_dimensions[index]) {
        joined_dimensions[index] = &dim.value();
        shape_and_rank_for_dim[index] =
            std::make_pair(shapes[shape.index()], shape.value().size());
        continue;
      }
      // Bail if the dimensions are neither equal nor 1.
      if (*joined_dimensions[index] != dim.value()) return {};
    }
  }
  // If the output is the same as one of the inputs just return that.
  if (llvm::is_splat(shape_and_rank_for_dim) &&
      shape_and_rank_for_dim[0].first) {
    return shape_and_rank_for_dim[0].first;
  }
  // Otherwise rematerialize the shape from the pieces we have.
  SmallVector<Value> elements;
  for (int i = 0; i != maxRank; ++i) {
    // 1 dimensions are filtered above, recreate the constant.
    if (!shape_and_rank_for_dim[i].first) {
      auto one = builder->getIntegerAttr(
          shapes[0].getType().cast<RankedTensorType>().getElementType(), 1);
      elements.push_back(builder->create<ConstantOp>(loc, one));
      continue;
    }
    // Extract from one of the shapes, accounting for the reverse indexing
    // performed by broadcast.
    Value index = builder->create<ConstantIndexOp>(
        loc, i - maxRank + shape_and_rank_for_dim[i].second);
    elements.push_back(builder->create<tensor::ExtractOp>(
        loc, shape_and_rank_for_dim[i].first, index));
  }
  return Value(builder->create<tensor::FromElementsOp>(loc, elements));
}


LogicalResult CstrBroadcastableOpLowering::matchAndRewrite(
    shape::CstrBroadcastableOp op, mlir::PatternRewriter& rewriter) const {
  ShapeComponentAnalysis shape_component_analysis;
  if (!isKnownBroadcastable(shape_component_analysis, op.getShapes(),
                            op.getShapes().front()))
    return failure();

  // Replace constraint with a true witness.
  rewriter.replaceOpWithNewOp<shape::ConstWitnessOp>(op, true);

  return success();
}


  void runOnFunction() override {
    FuncOp func = getFunction();

    MLIRContext* ctx = &getContext();
    mlir::RewritePatternSet patterns(ctx);

    // Rewrite constraints based on the symbolic shapes.
    patterns.insert<CstrBroadcastableOpLowering>(ctx);
    // Rewrite shape.broadcast based on the symbolic shapes.
    patterns.insert<BroadcastOpLowering>(ctx);

    // Rewrite broadcasts based on the symbolic shapes if enabled.
    if (!optimize_only_constraints)
      patterns.insert<DynamicBroadcastInDimOpLowering>(ctx);

    // Add shape dialect canonicalization patterns to fold shape operations
    // after constraints are replaced with constant witness.
    for (auto op : ctx->getRegisteredOperations()) {
      if (llvm::isa<shape::ShapeDialect>(op.getDialect()))
        op.getCanonicalizationPatterns(patterns, ctx);
    }

    (void)mlir::applyPatternsAndFoldGreedily(func, std::move(patterns));
  }


bool isKnownBroadcastable(ShapeComponentAnalysis& analysis,
                          ValueRange bcasted_shapes, Value output_shape) {
  auto output_shape_dims = analysis.GetValueInfo(output_shape);
  if (!output_shape_dims) return false;
  for (Value shape : bcasted_shapes) {
    auto shape_dims = analysis.GetValueInfo(shape);
    if (!shape_dims) return false;
    // Iterate backwards over the smallest input shape.
    for (auto zip : llvm::zip(llvm::reverse(*output_shape_dims),
                              llvm::reverse(*shape_dims))) {
      const auto& first = std::get<0>(zip);
      const auto& second = std::get<1>(zip);
      // TODO(ezhulenev): What to do with dimensions statically known to be
      // zero?
      // Numpy can only broadcast [0] with [1], however Tensorflow can broadcast
      // [0] with any dimension size, and produces dimension of size [0].
      // Currently we'll conservatively return failure and will not proceed with
      // a rewrite.
      if (first.isConstant(0) || second.isConstant(0)) return false;
      // If either shape has a static one dimension the broadcast will always
      // succeed.
      if (first.isConstant(1) || second.isConstant(1)) continue;
      // Otherwise dims have to be equal.
      if (first != second) return false;
    }
  }
  return true;
}


llvm::Optional<AffineMap> isNonExpandingBroadcast(
    ShapeComponentAnalysis& analysis, Value from, Value to_shape) {
  auto in_shape = analysis.GetShapeInfo(from);
  auto out_shape = analysis.GetValueInfo(to_shape);
  if (!in_shape || !out_shape) return {};

  SmallVector<AffineExpr> input_map_exprs;
  size_t rank = out_shape->size();
  MLIRContext* ctx = (*out_shape)[0].expr.getContext();
  size_t d = 0;
  auto affine_zero = getAffineConstantExpr(0, ctx);
  for (auto zip :
       llvm::zip(llvm::reverse(*in_shape), llvm::reverse(*out_shape))) {
    const auto& in = std::get<0>(zip);
    const auto& out = std::get<1>(zip);
    bool extend = in.isConstant(1) && !out.isConstant(1);
    input_map_exprs.push_back(extend ? affine_zero
                                     : getAffineDimExpr(rank - d - 1, ctx));
    ++d;

    // Bail if this is neither a known expansion nor a known non-expansion.
    if (!extend && in != out) return {};
  }
  // Any leading dimensions will be expanded.
  input_map_exprs.resize(in_shape->size(), affine_zero);
  std::reverse(input_map_exprs.begin(), input_map_exprs.end());
  return AffineMap::get(/*dimCount=*/rank,
                        /*symbolCount=*/0, input_map_exprs, ctx);
}


std::unique_ptr<FunctionPass> CreateSymbolicShapeOptimizationPass(
    bool constraints_only) {
  return std::make_unique<SymbolicShapeOptimizationPass>(constraints_only);
}


LogicalResult BroadcastOpLowering::matchAndRewrite(
    shape::BroadcastOp op, mlir::PatternRewriter& rewriter) const {
  ShapeComponentAnalysis shape_component_analysis;
  auto new_broadcast = simplifyBroadcast(
      shape_component_analysis, op.getShapes(), op.getLoc(), &rewriter);
  if (!new_broadcast) return failure();
  rewriter.replaceOp(op, {*new_broadcast});
  return success();
}


LogicalResult DynamicBroadcastInDimOpLowering::matchAndRewrite(
    mhlo::DynamicBroadcastInDimOp op, mlir::PatternRewriter& rewriter) const {
  MLIRContext* ctx = getContext();

  auto in_type = op.operand().getType().dyn_cast<RankedTensorType>();
  auto out_type = op.getResult().getType().dyn_cast<RankedTensorType>();
  if (!in_type || !out_type) return failure();

  // Check that broadcast is right-aligned (numpy style), so that operand
  // dimensions broadcasted to match inner-most dimensions of the output.
  auto bcast_dims = op.broadcast_dimensions().getValues<int64_t>();
  auto expected_bcast_dims = llvm::seq<int64_t>(
      out_type.getRank() - in_type.getRank(), out_type.getRank());
  if (!llvm::equal(bcast_dims, expected_bcast_dims)) return failure();

  ShapeComponentAnalysis shape_component_analysis;
  auto input_map = isNonExpandingBroadcast(
      shape_component_analysis, op.operand(), op.output_dimensions());
  if (!input_map) return failure();

  // Resolve dynamic output dimensions for the `linalg.init_tensor` operation.
  SmallVector<Value> output_dyn_dimensions;
  Location loc = op.getLoc();
  int64_t rank = out_type.getRank();
  for (size_t d = 0; d < rank; ++d) {
    int64_t output_dim = out_type.getShape()[d];

    // Skip static output dimensions, they will be resolved from the shape.
    if (output_dim >= 0) continue;

    // Resolve the dynamic size of the output dimension.
    Value output_dyn_dim = rewriter.create<tensor::ExtractOp>(
        loc, op.output_dimensions(),
        ValueRange{rewriter.create<ConstantIndexOp>(loc, d)});

    // Symbolic shape analysis might have given us an i32 or i64. Cast to index.
    if (!output_dyn_dim.getType().isIndex())
      output_dyn_dim = rewriter.create<IndexCastOp>(loc, output_dyn_dim,
                                                    rewriter.getIndexType());

    output_dyn_dimensions.push_back(output_dyn_dim);
  }

  // Create a linalg.tensor_init operation to initialize output.
  Value init = rewriter.create<linalg::InitTensorOp>(loc, output_dyn_dimensions,
                                                     out_type.getShape(),
                                                     out_type.getElementType());

  // Output indexing map is an identity with `rank` number of loops.
  AffineMap output_map = AffineMap::getMultiDimIdentityMap(rank, ctx);

  // All iterators are parallel.
  SmallVector<llvm::StringRef> iterator_types(rank, "parallel");

  rewriter.replaceOpWithNewOp<linalg::GenericOp>(
      op, /*resultTensorTypes=*/TypeRange{init.getType()},
      /*inputs=*/ValueRange{op.operand()},
      /*outputs=*/ValueRange{init},
      /*indexingMaps=*/llvm::makeArrayRef({*input_map, output_map}),
      /*iteratorTypes=*/iterator_types,
      [&](OpBuilder& nested_builder, Location nested_loc, ValueRange args) {
        nested_builder.create<linalg::YieldOp>(nested_loc, args[0]);
      });

  return success();
}


  explicit SymbolicShapeOptimizationPass(bool constraints_only) {
    this->optimize_only_constraints = constraints_only;
  }


CstrBroadcastableOpLowering::CstrBroadcastableOpLowering(MLIRContext* ctx)
    : Base(ctx) {}


LiteralString *hermes::evalToString(IRBuilder &builder, Literal *operand) {
  if (auto *str = llvh::dyn_cast<LiteralString>(operand))
    return str;
  if (auto *num = llvh::dyn_cast<LiteralNumber>(operand)) {
    char buf[NUMBER_TO_STRING_BUF_SIZE];
    auto len = numberToString(num->getValue(), buf, sizeof(buf));
    return builder.getLiteralString(StringRef(buf, len));
  }
  return nullptr;
}

Handle typeof applied to empty in InstSimplify

Summary:
Do not simplify `typeof` if it is applied to an invalid type. This
handles a case like the one in the added test, where `typeof` is called
on a literal empty in unreachable code.

Reviewed By: kodafb

Differential Revision: D31000173

fbshipit-source-id: 2d7f69cbcc9c1bb0a916585c07171089444c85dc


bool isNaN(Literal *lit) {
  if (auto *number = llvh::dyn_cast<LiteralNumber>(lit)) {
    return std::isnan(number->getValue());
  }
  return false;
}


Literal *hermes::evalBinaryOperator(
    BinaryOperatorInst::OpKind kind,
    IRBuilder &builder,
    Literal *lhs,
    Literal *rhs) {
  Type leftTy = lhs->getType();
  Type rightTy = rhs->getType();

  auto *leftLiteralNum = llvh::dyn_cast<LiteralNumber>(lhs);
  auto *rightLiteralNum = llvh::dyn_cast<LiteralNumber>(rhs);

  auto *leftNull = llvh::dyn_cast<LiteralNull>(lhs);
  auto *rightNull = llvh::dyn_cast<LiteralNull>(rhs);

  auto *leftUndef = llvh::dyn_cast<LiteralUndefined>(lhs);
  auto *rightUndef = llvh::dyn_cast<LiteralUndefined>(rhs);

  auto *leftStr = llvh::dyn_cast<LiteralString>(lhs);
  auto *rightStr = llvh::dyn_cast<LiteralString>(rhs);

  auto leftNaN = isNaN(lhs);
  auto rightNaN = isNaN(rhs);

  auto &ctx = builder.getModule()->getContext();

  using OpKind = BinaryOperatorInst::OpKind;

  if (leftNaN || rightNaN) {
    switch (kind) {
      case OpKind::EqualKind:
      case OpKind::StrictlyEqualKind:
      case OpKind::LessThanKind:
      case OpKind::LessThanOrEqualKind:
      case OpKind::GreaterThanKind:
      case OpKind::GreaterThanOrEqualKind:
        // Equality and order comparisons with NaN always evaluate to false,
        // even in cases like 'NaN == NaN' or 'NaN <= NaN'
        return builder.getLiteralBool(false);
      case OpKind::NotEqualKind:
      case OpKind::StrictlyNotEqualKind:
        // Inequality comparisons with NaN always evaluate to true, even in
        // cases like 'NaN != NaN' or 'NaN !== NaN'
        return builder.getLiteralBool(true);
      case OpKind::LeftShiftKind:
      case OpKind::RightShiftKind:
      case OpKind::UnsignedRightShiftKind:
        // The code for bitwise shift operators below properly handles
        // NaN, so we break out of the switch and go through to there
        break;
      case OpKind::AddKind:
        // If we're trying to add NaN with a string, then we need to perform
        // string concatenation with "NaN" and the string operand
        if (leftStr) {
          SmallString<256> result =
              buildString(ctx.toString(leftStr->getValue()), "NaN");
          return builder.getLiteralString(result.str());
        }

        if (rightStr) {
          SmallString<256> result =
              buildString("NaN", ctx.toString(rightStr->getValue()));
          return builder.getLiteralString(result.str());
        }

        // None of the operands are strings, so the expression evaluates
        // to NaN
        return builder.getLiteralNaN();
      case OpKind::SubtractKind:
      case OpKind::MultiplyKind:
      case OpKind::DivideKind:
      case OpKind::ModuloKind:
        // Binary arithmetic operations involving NaN evaluate to  NaN
        return builder.getLiteralNaN();
      case OpKind::OrKind:
      case OpKind::XorKind:
      case OpKind::AndKind:
        // The code for bitwise logical operators below properly handles
        // NaN, so we break out of the switch and go through to there
        break;
      default:
        // All handling of NaN is done is the current block. The rest of this
        // code assumes the literals are not NaN (except for bitwise shifts and
        // logical ops), so we break out of the function here, giving up on
        // finding a compile-time literal representation of the result.
        return nullptr;
    }
  }

  auto numericOrder = getNumericOrder(lhs, rhs);

  switch (kind) {
    case OpKind::EqualKind: // ==
      // Identical literals must be equal.
      if (lhs == rhs) {
        return builder.getLiteralBool(true);
      }

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Equal:
            return builder.getLiteralBool(true);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Unordered:
            break;
        }
      }

      // Handle string equality:
      if (leftStr && rightStr) {
        return builder.getLiteralBool(
            leftStr->getValue() == rightStr->getValue());
      }

      break;
    case OpKind::NotEqualKind: // !=
      // Identical operands can't be non-equal.
      if (lhs == rhs) {
        return builder.getLiteralBool(false);
      }

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Equal:
            return builder.getLiteralBool(false);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Unordered:
            break;
        }
      }
      // Handle string equality:
      if (leftStr && rightStr) {
        return builder.getLiteralBool(
            leftStr->getValue() != rightStr->getValue());
      }

      break;
    case OpKind::StrictlyEqualKind: // ===
      // Identical literals must be equal.
      if (lhs == rhs) {
        return builder.getLiteralBool(true);
      }

      // Operands of different types can't be strictly equal.
      if (disjointComparisonTypes(leftTy, rightTy))
        return builder.getLiteralBool(false);

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Equal:
            return builder.getLiteralBool(true);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Unordered:
            break;
        }
      }

      // Handle string equality:
      if (leftStr && rightStr) {
        return builder.getLiteralBool(
            leftStr->getValue() == rightStr->getValue());
      }

      break;
    case OpKind::StrictlyNotEqualKind: // !===
      // Identical operands can't be non-equal.
      if (lhs == rhs) {
        return builder.getLiteralBool(false);
      }

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Equal:
            return builder.getLiteralBool(false);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Unordered:
            break;
        }
      }

      // Handle string equality:
      if (leftStr && rightStr) {
        return builder.getLiteralBool(
            leftStr->getValue() != rightStr->getValue());
      }

      break;
    case OpKind::LessThanKind: // <
      // Handle comparison to self:
      if (!leftTy.isUndefinedType() && lhs == rhs)
        return builder.getLiteralBool(false);

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Equal:
            return builder.getLiteralBool(false);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Unordered:
            break;
        }
      }
      break;
    case OpKind::LessThanOrEqualKind: // <=
      // Handle comparison to self:
      if (!leftTy.isUndefinedType() && lhs == rhs)
        return builder.getLiteralBool(true);

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Equal:
            return builder.getLiteralBool(true);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Unordered:
            break;
        }
      }

      break;
    case OpKind::GreaterThanKind: // >
      // Handle comparison to self:
      if (!leftTy.isUndefinedType() && lhs == rhs)
        return builder.getLiteralBool(false);

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Equal:
            return builder.getLiteralBool(false);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Unordered:
            break;
        }
      }

      break;
    case OpKind::GreaterThanOrEqualKind: // >=
      // Handle comparison to self:
      if (!leftTy.isUndefinedType() && lhs == rhs)
        return builder.getLiteralBool(true);

      // Handle numeric comparisons:
      if (numericOrder.hasValue()) {
        switch (numericOrder.getValue()) {
          case NumericOrder::LessThan:
            return builder.getLiteralBool(false);
          case NumericOrder::Equal:
            return builder.getLiteralBool(true);
          case NumericOrder::GreaterThan:
            return builder.getLiteralBool(true);
          case NumericOrder::Unordered:
            break;
        }
      }

      break;
    case OpKind::LeftShiftKind: // <<  (<<=)
    case OpKind::RightShiftKind: // >>  (>>=)
    case OpKind::UnsignedRightShiftKind: { // >>> (>>>=)
      // Convert both operands to literal numbers if they can be.
      auto *lnum = evalToNumber(builder, lhs);
      auto *rnum = evalToNumber(builder, rhs);
      if (!lnum || !rnum) {
        // Can't be converted to a literal number.
        break;
      }
      uint32_t shiftCount = rnum->truncateToUInt32() & 0x1f;
      // Large enough to hold both int32_t and uint32_t values.
      int64_t result = 0;
      if (kind == OpKind::LeftShiftKind) {
        // Truncate to unsigned so that the shift doesn't happen on negative
        // values. Cast it to a 32-bit signed int to get the sign back, then
        // promote to 64 bits.
        result = static_cast<int32_t>(lnum->truncateToUInt32() << shiftCount);
      } else if (kind == OpKind::RightShiftKind) {
        result = static_cast<int64_t>(lnum->truncateToInt32() >> shiftCount);
      } else {
        result = static_cast<int64_t>(lnum->truncateToUInt32() >> shiftCount);
      }
      return builder.getLiteralNumber(result);
    }
    case OpKind::AddKind: { // +   (+=)
      // Handle numeric constants:
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            leftLiteralNum->getValue() + rightLiteralNum->getValue());
      }

      // Handle string concat:
      if (leftStr && rightStr) {
        SmallString<256> result = buildString(
            ctx.toString(leftStr->getValue()),
            ctx.toString(rightStr->getValue()));
        return builder.getLiteralString(result.str());
      }

      if (leftNull && rightNull) {
        return builder.getLiteralPositiveZero();
      }

      if (leftUndef && rightUndef) {
        return builder.getLiteralNaN();
      }

      if (leftNull) {
        if (rightLiteralNum) {
          return rhs;
        } else if (rightStr) {
          SmallString<256> result =
              buildString("null", ctx.toString(rightStr->getValue()));
          return builder.getLiteralString(result.str());
        }
      }

      if (rightNull) {
        if (leftLiteralNum) {
          return lhs;
        } else if (leftStr) {
          SmallString<256> result =
              buildString(ctx.toString(leftStr->getValue()), "null");
          return builder.getLiteralString(result.str());
        }
      }

      if (leftUndef) {
        if (rightLiteralNum) {
          return builder.getLiteralNaN();
        } else if (rightStr) {
          SmallString<256> result =
              buildString("undefined", ctx.toString(rightStr->getValue()));
          return builder.getLiteralString(result.str());
        }
      }

      if (rightUndef) {
        if (leftLiteralNum) {
          return builder.getLiteralNaN();
        } else if (leftStr) {
          SmallString<256> result =
              buildString(ctx.toString(leftStr->getValue()), "undefined");
          return builder.getLiteralString(result.str());
        }
      }

      break;
    }
    case OpKind::SubtractKind: // -   (-=)
      // Handle numeric constants:
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            leftLiteralNum->getValue() - rightLiteralNum->getValue());
      }

      break;

    case OpKind::MultiplyKind: // *   (*=)
      // Handle numeric constants:
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            leftLiteralNum->getValue() * rightLiteralNum->getValue());
      }

      if ((leftNull && rightLiteralNum) || (rightNull && leftLiteralNum) ||
          (leftNull && rightNull)) {
        if ((leftLiteralNum && std::signbit(leftLiteralNum->getValue())) ||
            (rightLiteralNum && std::signbit(rightLiteralNum->getValue()))) {
          return builder.getLiteralNegativeZero();
        }
        return builder.getLiteralPositiveZero();
      }

      break;
    case OpKind::DivideKind: // /   (/=)
      if (leftLiteralNum && rightLiteralNum) {
        // This relies on IEEE 754 double division. All modern compilers
        // implement this.
        return builder.getLiteralNumber(
            leftLiteralNum->getValue() / rightLiteralNum->getValue());
      }
      break;
    case OpKind::ModuloKind: // %   (%=)
      // Note that fmod differs slightly from the ES spec with regards to how
      // numbers not representable by double are rounded. This difference can be
      // ignored in practice, so most JS VMs use fmod.
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            std::fmod(leftLiteralNum->getValue(), rightLiteralNum->getValue()));
      }

      break;
    case OpKind::ExponentiationKind: // ** (**=)
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(hermes::expOp(
            leftLiteralNum->getValue(), rightLiteralNum->getValue()));
      }
      break;
    case OpKind::OrKind: // |   (|=)
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            leftLiteralNum->truncateToInt32() |
            rightLiteralNum->truncateToInt32());
      }

      break;
    case OpKind::XorKind: // ^   (^=)
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            leftLiteralNum->truncateToInt32() ^
            rightLiteralNum->truncateToInt32());
      }

      break;
    case OpKind::AndKind: // &   (&=)
      if (leftLiteralNum && rightLiteralNum) {
        return builder.getLiteralNumber(
            leftLiteralNum->truncateToInt32() &
            rightLiteralNum->truncateToInt32());
      }

      break;
    default:
      break;
  }

  return nullptr;
}


bool hermes::evalIsTrue(IRBuilder &builder, Literal *operand) {
  if (auto *lit = evalToBoolean(builder, operand))
    return lit->getValue();
  return false;
}


llvh::Optional<NumericOrder> getNumericOrder(Literal *LHS, Literal *RHS) {
  auto *L = llvh::dyn_cast<LiteralNumber>(LHS);
  auto *R = llvh::dyn_cast<LiteralNumber>(RHS);

  if (!L || !R)
    return llvh::None;

  double l = L->getValue();
  double r = R->getValue();

  if (l < r)
    return NumericOrder::LessThan;
  if (l > r)
    return NumericOrder::GreaterThan;
  if (std::isnan(l) || std::isnan(r))
    return NumericOrder::Unordered;
  return NumericOrder::Equal;
}


bool hermes::evalIsFalse(IRBuilder &builder, Literal *operand) {
  if (auto *lit = evalToBoolean(builder, operand))
    return !lit->getValue();
  return false;
}


SmallString<256> buildString(const StringRef &a, const StringRef &b) {
  SmallString<256> result;
  result.append(a);
  result.append(b);
  return result;
}


LiteralNumber *hermes::evalToInt32(IRBuilder &builder, Literal *operand) {
  // Eval to a number first, then truncate to a 32-bit int.
  LiteralNumber *lit = evalToNumber(builder, operand);
  if (!lit) {
    return nullptr;
  }
  double val = lit->getValue();
  return builder.getLiteralNumber(truncateToInt32(val));
}


LiteralNumber *hermes::evalToNumber(IRBuilder &builder, Literal *operand) {
  if (auto *numLiteral = llvh::dyn_cast<LiteralNumber>(operand)) {
    return numLiteral;
  }
  if (auto *boolLiteral = llvh::dyn_cast<LiteralBool>(operand)) {
    return builder.getLiteralNumber(boolLiteral->getValue());
  }
  if (operand->getType().isUndefinedType()) {
    return builder.getLiteralNaN();
  }
  if (operand->getType().isNullType()) {
    return builder.getLiteralPositiveZero();
  }
  return nullptr;
}


LiteralBool *hermes::evalToBoolean(IRBuilder &builder, Value *operand) {
  if (auto *L = llvh::dyn_cast<Literal>(operand)) {
    return evalToBoolean(builder, L);
  }

  Type OpTY = operand->getType();
  if (OpTY.isObjectType()) {
    return builder.getLiteralBool(true);
  }
  if (OpTY.isNullType() || OpTY.isUndefinedType()) {
    return builder.getLiteralBool(false);
  }
  return nullptr;
}


Literal *hermes::evalUnaryOperator(
    UnaryOperatorInst::OpKind kind,
    IRBuilder &builder,
    Literal *operand) {
  switch (kind) {
    case UnaryOperatorInst::OpKind::MinusKind:
      // Negate constant integers.
      switch (operand->getKind()) {
        case ValueKind::LiteralNumberKind:
          if (auto *literalNum = llvh::dyn_cast<LiteralNumber>(operand)) {
            auto V = -literalNum->getValue();
            return builder.getLiteralNumber(V);
          }
          break;
        case ValueKind::LiteralUndefinedKind:
          return builder.getLiteralNaN();
        case ValueKind::LiteralBoolKind:
          if (evalIsTrue(builder, operand)) {
            return builder.getLiteralNumber(-1);
          } else { // evalIsFalse(operand)
            return builder.getLiteralNegativeZero();
          }
        case ValueKind::LiteralNullKind:
          return builder.getLiteralNegativeZero();
        default:
          break;
      }
      break;
    case UnaryOperatorInst::OpKind::TypeofKind:
      switch (operand->getKind()) {
        case ValueKind::GlobalObjectKind:
        case ValueKind::LiteralNullKind:
          return builder.getLiteralString("object");
        case ValueKind::LiteralUndefinedKind:
          return builder.getLiteralString("undefined");
        case ValueKind::LiteralBoolKind:
          return builder.getLiteralString("boolean");
        case ValueKind::LiteralNumberKind:
          return builder.getLiteralString("number");
        case ValueKind::LiteralStringKind:
          return builder.getLiteralString("string");
        default:
          break;
      }
      break;

    case UnaryOperatorInst::OpKind::BangKind:
      if (evalIsTrue(builder, operand)) {
        return builder.getLiteralBool(false);
      }
      if (evalIsFalse(builder, operand)) {
        return builder.getLiteralBool(true);
      }
      break;

    case UnaryOperatorInst::OpKind::VoidKind:
      return builder.getLiteralUndefined();

    default:
      break;
  }

  return nullptr;
}


bool disjointComparisonTypes(Type A, Type B) {
  if (!A.isPrimitive() || !B.isPrimitive())
    return false;

  // Check if types are disjoint.
  return Type::intersectTy(A, B).isNoType();
}


LiteralBool *hermes::evalToBoolean(IRBuilder &builder, Literal *operand) {
  bool value;
  switch (operand->getKind()) {
    case ValueKind::LiteralNullKind:
    case ValueKind::LiteralUndefinedKind:
      value = false;
      break;
    case ValueKind::LiteralBoolKind:
      value = cast<LiteralBool>(operand)->getValue();
      break;
    case ValueKind::LiteralNumberKind: {
      const auto n = cast<LiteralNumber>(operand)->getValue();
      value = !std::isnan(n) && n != 0.0;
      break;
    }
    case ValueKind::LiteralStringKind:
      value = !cast<LiteralString>(operand)->getValue().str().empty();
      break;
    default:
      return nullptr;
  }

  return builder.getLiteralBool(value);
}


bool Socket::readyForOutput()
{
    //if (!isssl) {
        return BaseSocket::readyForOutput();
    //}

    //cant do this on a blocking ssl socket as far as i can work out

    //return true;
}

Fix bug #707 cert hostnames not being checked
- only happened when openssl v1.1 is used


void Socket::setPort(int port) {
    my_port = port;
}


int Socket::readFromSocket(char *buff, int len, unsigned int flags, int timeout, bool check_first, bool honour_reloadconfig)
{
    if (len == 0)  // nothing to read
         return 0;
    if (!isssl) {
        return BaseSocket::readFromSocket(buff, len, flags, timeout, check_first, honour_reloadconfig);
    }

    // first, return what's left from the previous buffer read, if anything
    int cnt = len;
    int tocopy = 0;
    if ((bufflen - buffstart) > 0) {
#ifdef NETDEBUG
        std::cout << thread_id << "Socket::readFromSocket: data already in buffer; bufflen: " << bufflen << " buffstart: " << buffstart << std::endl;
#endif
        tocopy = len;
        if ((bufflen - buffstart) < len)
            tocopy = bufflen - buffstart;
        memcpy(buff, buffer + buffstart, tocopy);
        cnt -= tocopy;
        buffstart += tocopy;
        buff += tocopy;
        if (cnt == 0)
            return len;
    }

    int rc;
    while (cnt > 0) {
    //if (check_first) {
          //if(!bcheckSForInput(timeout))
            //return -1;
   //}
//    while (true)
        bool inbuffer;
        ERR_clear_error();
        if(true) {   //   was if (cnt > 4095)
            inbuffer = false;
           rc = SSL_read(ssl, buff, cnt);        //  data larger than SSL buffer so ok to read directly into output buffer
        } else {
            inbuffer = true;
           rc = SSL_read(ssl, buffer, 4096);   // read into socket buffer to flush SSL buffer
        }

        if (rc < 0) {
            s_errno = errno;
            log_ssl_errors("ssl_read failed %s", "");
#ifdef NETDEBUG
        std::cout << thread_id << "ssl_read failed" << s_errno << " failed to read " << cnt << " bytes" << std::endl;
#endif
            rc = 0;
        }
        if (rc == 0) { // eof
             ishup = true;
             return len - cnt;
             }

        if (inbuffer) {
#ifdef NETDEBUG
        std::cout << thread_id << "Inbuffer SSL read to return " << cnt << " bytes" << std::endl;
#endif

           buffstart = 0;
           bufflen = rc;
           if ((bufflen - buffstart) > 0) {
              tocopy = cnt;
              if ((bufflen - buffstart) < cnt)
              tocopy = bufflen - buffstart;
              memcpy(buff, buffer + buffstart, tocopy);
              cnt -= tocopy;
              buffstart += tocopy;
              buff += tocopy;
#ifdef NETDEBUG
        std::cout << thread_id << "Inbuffer SSL read to returned " << tocopy << " bytes" << std::endl;
#endif
           }
         } else {
        buff += rc;
        cnt -= rc;
         }
 //       break;
    }

//    return rc + tocopy;
      return len;
}


std::string Socket::getLocalIP() {
    char res[INET_ADDRSTRLEN];
    return inet_ntop(AF_INET,&my_adr.sin_addr, res, sizeof(res));
}


bool Socket::checkForInput()
{
    if (!isssl) {
        return BaseSocket::checkForInput();
    }
#ifdef NETDEBUG
    std::cout << thread_id << "checking for input on ssl connection (non blocking)" << std::endl;
#endif
    if ((bufflen - buffstart) > 0) {
#ifdef NETDEBUG
        std::cout << thread_id << "found input on ssl connection" << std::endl;
#endif
        return true;
    }

    if (!BaseSocket::checkForInput())
        return false;

    //see if we can do an ssl read of 1 byte
//    char buf[1];

    //int rc = SSL_peek(ssl, buf, 1);
    int rc = SSL_pending(ssl);

    if (rc < 1) {
#ifdef NETDEBUG
        std::cout << thread_id << "no pending data on ssl connection SSL_pending " << rc << std::endl;
#endif
        return false;
    }

#ifdef NETDEBUG
    std::cout << thread_id << "found data on ssl connection" << std::endl;
#endif

    return true;
}


int Socket::readChunk( char *buffin, int maxlen, int timeout){
    if (chunk_to_read == 0)     // need to read chunk size
    {
        char size[40];
        ieof = false;
        int len = getLine(size, 38, timeout);
        if (len < 2) {   // min valid length is 2 i.e.  "0\r"
            chunkError = true;
            return -1;
        }
#ifdef CHUNKDEBUG
        std::cerr << thread_id << "readChunk  size=" << size << std::endl;
#endif
        String l = size;
        l.chop();
        String t = l.before(";");
        if (t.length() > 0) {
            if (l.endsWith("; ieof")) {
                ieof = true;
            }
            l = t;
        }
        chunk_to_read = l.hexToInteger();
#ifdef CHUNKDEBUG
        std::cerr << thread_id << "readChunk  chunk_to_read =" << chunk_to_read << std::endl;
#endif
    }

    int clen = chunk_to_read;
    if (clen > maxlen) {
        clen = maxlen;
    }
    int rc = 0;
#ifdef CHUNKDEBUG
    std::cerr << thread_id << "readChunk  max_read =" << clen << std::endl;
#endif

    if(clen == 0) {
        chunked_trailer = "";
        char trailer[32000];
        int len = 3;
        while( len > 2) {
            len = getLine(trailer, 31900, timeout);
            if (len > 2) {
                chunked_trailer += trailer;
                chunked_trailer += "\n";
            }
        }
        return 0;
    }

    if (clen > 0) {
        rc = readFromSocketn(buffin, clen, 0, timeout);
#ifdef CHUNKDEBUG
        std::cerr << thread_id << "readChunk  read " << rc << std::endl;
#endif
        if (rc < 0) {
            chunkError = true;
            return -1;
        }
        chunk_to_read -= rc;
    }
    if (chunk_to_read > 0)    // there is more to read in this chunk - so do not check for trailing \r\n
        return rc;
    char ts[2];
    int len = readFromSocketn(ts, 2, 0, timeout);
    if (len == 2 && ts[0] == '\r' && ts[1] == '\n') {
        return rc;
    } else {
        chunkError = true;
#ifdef CHUNKDEBUG
        std::cerr << thread_id << "readChunk - tail in error" << std::endl;
#endif
        return -1;
    }
}


Socket::Socket(int fd)
        : BaseSocket(fd) {
    memset(&my_adr, 0, sizeof my_adr);
    memset(&peer_adr, 0, sizeof peer_adr);
    my_adr.sin_family = AF_INET;
    peer_adr.sin_family = AF_INET;
    peer_adr_length = sizeof(struct sockaddr_in);
    int f = 1;

    int res = setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int));
    if (res < 0) s_errno = errno;
    my_port = 0;
    chunkError = false;

#ifdef __SSLMITM
    ssl = NULL;
    ctx = NULL;
    isssl = false;
    issslserver = false;
#else
    isssl = false;
#endif
}


int Socket::readFromSocketn(char *buff, int len, unsigned int flags, int timeout)
{
    return readFromSocket(buff, len, flags, timeout, true, false);

#ifdef NODEF
    if (!isssl) {
        return BaseSocket::readFromSocketn(buff, len, flags, timeout);
    }

    int cnt, rc;
    cnt = len;

    // first, return what's left from the previous buffer read, if anything
    if ((bufflen - buffstart) > 0) {
#ifdef NETDEBUG
        std::cout << thread_id << "Socket::readFromSocketn: data already in buffer; bufflen: " << bufflen << " buffstart: " << buffstart << std::endl;
#endif
        int tocopy = len;
        if ((bufflen - buffstart) < len)
            tocopy = bufflen - buffstart;
        memcpy(buff, buffer + buffstart, tocopy);
        cnt -= tocopy;
        buffstart += tocopy;
        buff += tocopy;
        if (cnt == 0)
            return len;
    }

    while (cnt > 0) {
    //    try {
            //bcheckSForInput(timeout);        //  this may be wrong - why is data not being read into socket buffer????
    //    } catch (std::exception &e) {
     //       return -1;
     //   }
        ERR_clear_error();
        rc = SSL_read(ssl, buff, cnt);
#ifdef NETDEBUG
        std::cout << thread_id << "ssl read said: " << rc << std::endl;
#endif

        if (rc < 0) {
       //     if (errno == EINTR) {
         //       continue;
           // }
            log_ssl_errors("ssl_read failed %s", "");
           s_errno = errno;
            return -1;
        }
        if (rc == 0) { // eof
             ishup = true;
            return len - cnt;
        }
        buff += rc;
        cnt -= rc;
    }
    return len;
#endif
}


int Socket::drainChunk(int timeout)    // reads chunks until 0 len chunk or timeout
{
    char buff[32000];
    int tot_size = 0;
    int csize = 1;
    while (csize > 0) {
        csize = readChunk(buff,32000, timeout);
        if (!(csize > -1 )) {
#ifdef CHUNKDEBUG
            std::cerr << thread_id << "drainChunk - error" << std::endl;
#endif
            return -1;
        }
        tot_size += csize;
    }
#ifdef CHUNKDEBUG
    std::cerr << thread_id << "drainChunk  tot_size=" << tot_size << std::endl;
#endif
    return tot_size;
}


bool Socket::isSsl()
{
    return isssl;
}


bool Socket::getIeof() {
    return ieof;
}


bool Socket::writeString(const char *line) //throw(std::exception)
{
    int l = strlen(line);
    return writeToSocket(line, l, 0, timeout);
}


long Socket::checkCertValid(String &hostname)
{
    //check we have a certificate
    X509 *peerCert = SSL_get_peer_certificate(ssl);
    if (peerCert == NULL) {
        return -1;
    }
    X509_free(peerCert);

#if OPENSSL_VERSION_NUMBER < 0x10100000L
#else
// section for openssl1.1
X509_VERIFY_PARAM *param;
param = X509_VERIFY_PARAM_new() ;
X509_VERIFY_PARAM_set1_host(param,hostname.c_str(), hostname.length());
SSL_CTX_set1_param(ctx,param);
X509_VERIFY_PARAM_free(param);
#endif
    return SSL_get_verify_result(ssl);
}


int Socket::getLine(char *buff, int size, int timeout, bool honour_reloadconfig, bool *chopped, bool *truncated)
{
try {
    if (!isssl) {
        return BaseSocket::getLine(buff, size, timeout, honour_reloadconfig, chopped, truncated);
    }

    // first, return what's left from the previous buffer read, if anything
    int i = 0;
    if ((bufflen - buffstart) > 0) {
        /*#ifdef NETDEBUG
        std::cout << thread_id << "data already in buffer; bufflen: " << bufflen << " buffstart: " << buffstart << std::endl;
#endif*/
        int tocopy = size - 1;
        if ((bufflen - buffstart) < tocopy)
            tocopy = bufflen - buffstart;
        char *result = (char *)memccpy(buff, buffer + buffstart, '\n', tocopy);
        if (result != NULL) {
            // indicate that a newline was chopped off, if desired
            if (chopped)
                *chopped = true;
            *(--result) = '\0';
            buffstart += (result - buff) + 1;
            return result - buff;
        } else {
            i += tocopy;
            buffstart += tocopy;
        }
    }
    while (i < (size - 1)) {
        buffstart = 0;
        bufflen = 0;
 //       try {
 //            checkForInput(timeout, honour_reloadconfig);
 //       } catch (std::exception &e) {
//            throw std::runtime_error(std::string("Can't read from socket: ") + strerror(errno)); // on error
 //       }
//        if( bcheckSForInput(timeout))
            bufflen = SSL_read(ssl, buffer, 4096);
#ifdef NETDEBUG
//std::cout << thread_id << "read into buffer; bufflen: " << bufflen <<std::endl;
#endif
        if (bufflen < 0) {
  //          if (errno == EINTR ) {
   //             continue;
    //        }
            std::cout << thread_id << "SSL_read failed with error " << SSL_get_error(ssl, bufflen) << std::endl;
            log_ssl_errors("ssl_read failed %s", "");
            return -1;
//            throw std::runtime_error(std::string("Can't read from ssl socket")); //strerror(errno));  // on error
        }
        //if socket closed...
        if (bufflen == 0) {
            buff[i] = '\0'; // ...terminate string & return what read
            if (truncated)
                *truncated = true;
            return i;
        }
        int tocopy = bufflen;
        if ((i + bufflen) > (size - 1))
            tocopy = (size - 1) - i;
        char *result = (char *)memccpy(buff + i, buffer, '\n', tocopy);
        if (result != NULL) {
            // indicate that a newline was chopped off, if desired
            if (chopped)
                *chopped = true;
            *(--result) = '\0';
            buffstart += (result - (buff + i)) + 1;
            return i + (result - (buff + i));
        }
        i += tocopy;
    }
    // oh dear - buffer end reached before we found a newline
    buff[i] = '\0';
    if (truncated)
        *truncated = true;
    return i;
    } catch (...) {
    return -1;    }
}


std::string Socket::getPeerIP() {
    char res[INET_ADDRSTRLEN];
    return inet_ntop(AF_INET,&peer_adr.sin_addr, res, sizeof(res));
}


Socket::Socket(int newfd, struct sockaddr_in myip, struct sockaddr_in peerip)
        : BaseSocket(newfd) {
    memset(&my_adr, 0, sizeof my_adr); // ***
    memset(&peer_adr, 0, sizeof peer_adr); // ***
    my_adr.sin_family = AF_INET; // *** Fix suggested by
    peer_adr.sin_family = AF_INET; // *** Christopher Weimann
    my_adr = myip;
    peer_adr = peerip;
    peer_adr_length = sizeof(struct sockaddr_in);
    int f = 1;

    int res = setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int));
    if (res < 0) s_errno = errno;
    my_port = 0;
    chunkError = false;

#ifdef __SSLMITM
    ssl = NULL;
    ctx = NULL;
    isssl = false;
    issslserver = false;
#else
    isssl = false;
#endif
}


int Socket::bind(int port) {
    int len = sizeof my_adr;
    int i = 1;

    setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));

    my_adr.sin_port = htons(port);
    my_port = port;

    return ::bind(sck, (struct sockaddr *) &my_adr, len);
}


bool Socket::bcheckForInput(int timeout)
{
    if (!isssl) {
        return BaseSocket::bcheckForInput(timeout);
    }
    return true;
}


bool Socket::writeChunk( char *buffout, int len, int timeout){
    std::stringstream stm;
    stm << std::hex << len;
    std::string hexs (stm.str());
    //int lw;
    hexs += "\r\n";
#ifdef NETDEBUG
    std::cerr << thread_id << "writeChunk  size=" << hexs << std::endl;
#endif
    if(writeString(hexs.c_str()) && writeToSocket(buffout,len,0,timeout) && writeString("\r\n"))
        return true;
    return false;
};


Socket::Socket() {
    sck = socket(AF_INET, SOCK_STREAM, 0);
    if (sck < 0) {
        s_errno = errno;
    } else {
        memset(&my_adr, 0, sizeof my_adr);
        memset(&peer_adr, 0, sizeof peer_adr);
        my_adr.sin_family = AF_INET;
        peer_adr.sin_family = AF_INET;
        peer_adr_length = sizeof(struct sockaddr_in);
        int f = 1;

        if (sck > 0)
            setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &f, sizeof(int));

        my_port = 0;
        chunkError = false;

#ifdef __SSLMITM
        ssl = NULL;
        ctx = NULL;
        isssl = false;
        issslserver = false;
#else
        isssl = false;
#endif
    }
}


int Socket::checkCertHostname(const std::string &_hostname)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    String hostname = _hostname;

    X509 *peercertificate = SSL_get_peer_certificate(ssl);
    if (peercertificate == NULL) {
#ifdef NETDEBUG
        std::cout << thread_id << "unable to get certificate for " << hostname << std::endl;
#endif
        return -1;
    }
    //force to lower case as domain names are not case sensetive
    hostname.toLower();

#ifdef NETDEBUG
    std::cout << thread_id << "checking certificate" << hostname << std::endl;
    std::cout << thread_id << "Checking hostname against subjectAltNames" << std::endl;
#endif

    bool matched = false;
    bool hasaltname = false;

    //check the altname extension for additional valid names
    STACK_OF(GENERAL_NAME) *gens = NULL;
    gens = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(peercertificate, NID_subject_alt_name, 0, 0);
    int r = sk_GENERAL_NAME_num(gens);
    for (int i = 0; i < r; ++i) {
        const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);

        //if its not a dns entry we really dont care about it
        if (gn->type != GEN_DNS) {
            continue;
        }

        //only mark hasaltname as true if it has a DNS altname
        hasaltname = true;

        //an ASN1_IA5STRING is a define of an ASN1_STRING so we can do it this way
        unsigned char *nameutf8;
        int len = ASN1_STRING_to_UTF8(&nameutf8, gn->d.ia5);
        if (len < 0) {
            break;
        }

        String altname = std::string((char *)nameutf8, len);
        OPENSSL_free(nameutf8);

        //force to lower case as domain names are not case sensetive
        altname.toLower();

#ifdef NETDEBUG
        std::cout << thread_id << "checking against alt name " << altname << std::endl;
#endif

        if (hostname.compare(altname) == 0) {
            matched = true;
            break;
        } else if (altname.contains("*")) {
#ifdef NETDEBUG
            std::cout << thread_id << "Wildcard certificate is in use" << std::endl;
#endif
            String  anend;
            anend = altname.after("*"); // need to keep the "."
            if (hostname.endsWith(anend)) {
                bool part_match = true;
                String anstart = altname.before("*");
                String t = hostname.before(anend.c_str());
                if( anstart.length() > 0) {             // if something before * we must also match this
                  if( hostname.startsWith(anstart)) {
                    t = t.after(anstart.c_str());
                  } else {
                      part_match = false;    // even though after * matches, no match on before * - so cannot match
                   }
                 }
                 //    t now contains what is matched by the '*"  - this must not contain a '.'
                 if (part_match && !t.contains(".")) {
                   matched = true;
                   break;
                }
            }
        }
    }
    sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);

    if (matched) {
        X509_free(peercertificate);
        return 0;
    } else if (hasaltname) {
        X509_free(peercertificate);
        return -1;
    }

#ifdef NETDEBUG
    std::cout << thread_id << "checking hostname against the following common names" << std::endl;
#endif

    X509_NAME *name = X509_get_subject_name(peercertificate);

    int current_entry = -1;
    while (1) {

        //get the common name from the certificate
        current_entry = X509_NAME_get_index_by_NID(name, NID_commonName, current_entry);
        if (current_entry == -1) {
            //if we've run out of common names then move on to altnames
            break;
        }

        //X509_NAME_get_entry result must not be freed
        X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, current_entry);

        ASN1_STRING *asn1name = X509_NAME_ENTRY_get_data(entry);

        unsigned char *nameutf8;
        int len = ASN1_STRING_to_UTF8(&nameutf8, asn1name);
        if (len < 0) {
            break;
        }
        String commonname = std::string((char *)nameutf8, len);

        OPENSSL_free(nameutf8);

        //force to lower case as domain names are not case sensetive
        commonname.toLower();

#ifdef NETDEBUG
        std::cout << thread_id << "checking against common name " << commonname << std::endl;
#endif

        //compare the hostname to the common name
        if (hostname.compare(commonname) == 0) {
            matched = true;
            break;
        }
        //see if its a wildcard certificate
        else if (commonname.startsWith("*.")) {
#ifdef NETDEBUG
            std::cout << thread_id << "Wildcard certificate is in use" << std::endl;
#endif
            commonname = commonname.after("*"); // need to keep the "."

            if (hostname.endsWith(commonname)) {
                matched = true;
                break;
            }
        }
    }

    if (matched) {
        X509_free(peercertificate);
        return 0;
    }
#else  // is openssl v1.1 or above
    return 0;    //TODO
#endif
    return -1;
}


void Socket::close()
{
    if (isssl) {
        stopSsl();
    }
    BaseSocket::close();
}


bool Socket::writeString(std::string line)
{
    int l = line.length();
    return writeToSocket(line.c_str(), l, 0, timeout);
}


int Socket::startSslClient(const std::string &certificate_path, String hostname)
{
    if (isssl) {
        stopSsl();
    }

    ERR_clear_error();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    ctx = SSL_CTX_new(SSLv23_client_method());
#else
    ctx = SSL_CTX_new(TLS_client_method());
#endif

    if (ctx == NULL) {
#ifdef NETDEBUG
        std::cout << thread_id << "Error ssl context is null (check that openssl has been inited)" << std::endl;
#endif
        log_ssl_errors("Error ssl context is null for %s", hostname.c_str());
        return -1;
    }

    //set the timeout for the ssl session
    if (SSL_CTX_set_timeout(ctx, 130l) < 1) {
            SSL_CTX_free(ctx);
            ctx = NULL;
        return -1;
    }

    //load certs
    ERR_clear_error();
    if (certificate_path.length()) {
        if (!SSL_CTX_load_verify_locations(ctx, NULL, certificate_path.c_str())) {
#ifdef NETDEBUG
            std::cout << thread_id << "couldnt load certificates" << std::endl;
#endif
            log_ssl_errors("couldnt load certificates from %s", certificate_path.c_str());
            //tidy up
            SSL_CTX_free(ctx);
            ctx = NULL;
            return -2;
        }
    } else if (!SSL_CTX_set_default_verify_paths(ctx)) //use default if no certPpath given
    {
#ifdef NETDEBUG
        std::cout << thread_id << "couldnt load certificates" << std::endl;
#endif
            log_ssl_errors("couldnt load default certificates for %s", hostname.c_str());
        //tidy up
        SSL_CTX_free(ctx);
        ctx = NULL;
        return -2;
    }

    // add validation params
    ERR_clear_error();
    X509_VERIFY_PARAM *x509_param = X509_VERIFY_PARAM_new();
    if (!x509_param) {
        log_ssl_errors("couldnt add validation params for %s", hostname.c_str());
        //X509_VERIFY_PARAM_free(x509_param);
            SSL_CTX_free(ctx);
            ctx = NULL;
        return -2;
    }

    ERR_clear_error();
    if (!X509_VERIFY_PARAM_set_flags(x509_param, X509_V_FLAG_TRUSTED_FIRST)) {
        log_ssl_errors("couldnt add validation params for %s", hostname.c_str());
        X509_VERIFY_PARAM_free(x509_param);
            SSL_CTX_free(ctx);
            ctx = NULL;
        return -2;
    }

    ERR_clear_error();
    if (!SSL_CTX_set1_param(ctx, x509_param)) {
        log_ssl_errors("couldnt add validation params for %s", hostname.c_str());
        X509_VERIFY_PARAM_free(x509_param);
            SSL_CTX_free(ctx);
            ctx = NULL;
        return -2;
    }

    X509_VERIFY_PARAM_free(x509_param);     // try not freeing this as SSL_CTX_free seems to be ring to free it

    //hand socket over to ssl lib
    ERR_clear_error();
    ssl = SSL_new(ctx);
    SSL_set_options(ssl, SSL_OP_ALL);
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
    SSL_set_connect_state(ssl);

    //fcntl(this->getFD() ,F_SETFL, O_NONBLOCK); // blocking mode used currently
    SSL_set_fd(ssl, this->getFD());
    SSL_set_tlsext_host_name(ssl, hostname.c_str());
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#else
  X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl),hostname.c_str(),0);
#endif

    //make io non blocking as select wont tell us if we can do a read without blocking
    //BIO_set_nbio(SSL_get_rbio(ssl),1l);  // blocking mode used currently
    //BIO_set_nbio(SSL_get_wbio(ssl),1l); // blocking mode used currently
    ERR_clear_error();
    int rc = SSL_connect(ssl);
    if (rc < 0) {
        log_ssl_errors("ssl_connect failed to %s", hostname.c_str());
#ifdef NETDEBUG
        std::cout << thread_id << "ssl_connect failed with error " << SSL_get_error(ssl, rc) << std::endl;
#endif
        // tidy up
        SSL_free(ssl);
        ssl = NULL;
        SSL_CTX_free(ctx);
        ctx = NULL;
        return -3;
    }

    //should be safer to do this last as nothing will ever try to use a ssl socket that isnt fully setup
    isssl = true;
    issslserver = false;
    return 0;
}


int Socket::loopChunk(int timeout)    // reads chunks and sends back until 0 len chunk or timeout
{
    char buff[32000];
    int tot_size = 0;
    int csize = 1;
    while (csize > 0) {
        csize = readChunk(buff,32000, timeout);
        if (csize == 0)     // end chunk
        {
            if (!writeChunkTrailer(chunked_trailer))
            {
#ifdef CHUNKDEBUG
                std::cerr << thread_id << "loopChunk - error in writing chunk trailer" << std::endl;
#endif
                return -1;

            };
#ifdef CHUNKDEBUG
            std::cerr << thread_id << "loopChunk  tot_size=" << tot_size << std::endl;
#endif
            return tot_size;
        }
        if (!(csize > 0 && writeChunk(buff,csize,timeout))) {
#ifdef CHUNKDEBUG
            std::cerr << thread_id << "loopChunk - error" << std::endl;
#endif
            return -1;
        }
        tot_size += csize;
    }
    return -1;  // should never get here!
}


bool Socket::writeToSocket(const char *buff, int len, unsigned int flags, int timeout, bool check_first, bool honour_reloadconfig)
{
    if (len == 0)   // nothing to write
        return true;
    if (!isssl) {
        return BaseSocket::writeToSocket(buff, len, flags, timeout, check_first, honour_reloadconfig);
    }

    int actuallysent = 0;
    int sent;
    while (actuallysent < len) {
       if (check_first) {
    //        try {
                if(!breadyForOutput(timeout))
                   return false;
     //       } catch (std::exception &e) {
      //          return false;
       //     }
        }
        ERR_clear_error();
        sent = SSL_write(ssl, buff + actuallysent, len - actuallysent);
        if (sent < 0) {
    //        if (errno == EINTR ) {
    //            continue; // was interupted by signal so restart
    //        }
            s_errno = errno;
            String serr(s_errno);
            log_ssl_errors("ssl_write failed - error ",serr.c_str());
            return false;
        }
        if (sent == 0) {
            ishup = true;
            return false; // other end is closed
        }
        actuallysent += sent;
    }
    return true;
}


void Socket::reset() {
#ifdef __SSLMITM
    if (isssl) {
        stopSsl();
    }
#endif //__SSLMITM
    this->baseReset();

    sck = socket(AF_INET, SOCK_STREAM, 0);
    if (sck < 0) {
        s_errno = errno;
        return;
    }

    memset(&my_adr, 0, sizeof my_adr);
    memset(&peer_adr, 0, sizeof peer_adr);
    my_adr.sin_family = AF_INET;
    peer_adr.sin_family = AF_INET;
    peer_adr_length = sizeof(struct sockaddr_in);
    infds[0].fd = sck;
    outfds[0].fd = sck;

    chunkError = false;
    chunk_to_read = 0;

}


void Socket::resetChunk() {
    chunk_to_read = 0;
    chunked_trailer = "";
    ieof = false;
}


int Socket::connect(const std::string &ip, int port) {
    reset();   // do it anyway as we need sck to be allocated

    if (sck < 0) // socket creation error
    {
        return -1;
    }

    int len = sizeof my_adr;
    peer_adr.sin_port = htons(port);
    inet_aton(ip.c_str(), &peer_adr.sin_addr);
    my_port = port;
    // make non-blocking for connect only so that we can timeout connect
    fcntl(sck, F_SETFL, O_NONBLOCK);
    s_errno = 0;
    errno = 0;
    int ret = ::connect(sck, (struct sockaddr *) &peer_adr, len);
    if (ret < 0 && errno == EINPROGRESS) ret = 0;
    else s_errno = errno;
    if (ret == 0) {
        int rc = poll(outfds, 1, timeout);
        if (rc == 0) {
            timedout = true;
            ret = -1;
        } else if (rc < 0) {
            s_errno = errno;
            ret = -1;
        } else {  // ret == 1
            int so_error;
            socklen_t len = sizeof so_error;
            getsockopt(sck, SOL_SOCKET, SO_ERROR, &so_error, &len);
            if (so_error != 0) {
                sockerr = true;
                s_errno = so_error;
                ret = -1;
            } else {
                ret = 0;
            }
        }
    }
    fcntl(sck, F_SETFL, 0);  // make blocking again
    if (ret < 0) close();
    return ret;
}


int Socket::getPort() {
    return my_port;
}


bool Socket::writeChunkTrailer( String &trailer) {
    std::string hexs ("0\r\n");
#ifdef CHUNKDEBUG
    std::cerr << thread_id << "writeChunk  size=" << hexs << std::endl;
#endif
    if(writeString(hexs.c_str()) && writeToSocket(trailer.c_str(),trailer.length(),0,timeout) && writeString("\r\n"))
        return true;
    return false;
};


unsigned long int Socket::getPeerSourceAddr() {
    return (unsigned long int) ntohl(peer_adr.sin_addr.s_addr);
}


bool Socket::breadyForOutput(int timeout)
{
    //if (!isssl) {
        return BaseSocket::breadyForOutput(timeout);
    //}
    //return true;
}


int Socket::startSslServer(X509 *x, EVP_PKEY *privKey, std::string &set_cipher_list)
{

    if (isssl) {
        stopSsl();
    }

    // set ssl to NULL
    ssl = NULL;

    //setup the ssl server ctx
#if OPENSSL_VERSION_NUMBER < 0x10100000L
    ctx = SSL_CTX_new(SSLv23_server_method());
#else
    ctx = SSL_CTX_new(TLS_server_method());
#endif

    if (ctx == NULL) {
#ifdef NETDEBUG
        //syslog(LOG_ERR, "error creating ssl context\n");
        std::cout << thread_id << "Error ssl context is null (check that openssl has been inited)" << std::endl;
#endif
        return -1;
    }

    //set the timeout to match firefox
    if (SSL_CTX_set_timeout(ctx, 130l) < 1) {
        cleanSsl();
        return -1;
    }

    //set the ctx to use the certificate
    if (SSL_CTX_use_certificate(ctx, x) < 1) {
#ifdef NETDEBUG
        //syslog(LOG_ERR, "error creating ssl context\n");
        std::cout << thread_id << "Error using certificate" << std::endl;
#endif
        cleanSsl();
        return -1;
    }

    if (set_cipher_list.length() > 0)
        SSL_CTX_set_cipher_list(ctx, set_cipher_list.c_str());

    //set the ctx to use the private key
    if (SSL_CTX_use_PrivateKey(ctx, privKey) < 1) {
#ifdef NETDEBUG
        //syslog(LOG_ERR, "error creating ssl context\n");
        std::cout << thread_id << "Error using private key" << std::endl;
#endif
        cleanSsl();
        return -1;
    }

    //setup the ssl session
    ERR_clear_error();
    ssl = SSL_new(ctx);
    SSL_set_options(ssl, SSL_OP_ALL);
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
    SSL_set_accept_state(ssl);

    ERR_clear_error();
    if(!SSL_set_fd(ssl, this->getFD())) {
#ifdef NETDEBUG
        std::cout << thread_id << "Error setting ssl fd connection" << std::endl;
#endif
        log_ssl_errors("ssl_set_fd failed to client %s", "");
        cleanSsl();
        return -1;
    };

    //make io non blocking as select wont tell us if we can do a read without blocking

    ERR_clear_error();
    if (SSL_accept(ssl) < 0) {
#ifdef NETDEBUG
        //syslog(LOG_ERR, "error creating ssl context\n");
        std::cout << thread_id << "Error accepting ssl connection" << std::endl;
#endif
        log_ssl_errors("ssl_accept failed to client %s", "");
        cleanSsl();
        return -1;
    }

    ERR_clear_error();
    if (SSL_do_handshake(ssl) < 0) {
#ifdef NETDEBUG
        //syslog(LOG_ERR, "error creating ssl context\n");
        std::cout << thread_id << "Error doing ssl handshake" << std::endl;
#endif
        log_ssl_errors("ssl_handshake failed to client %s", "");
        cleanSsl();
        return -1;
    }
    isssl = true;
    issslserver = true;
    return 0;
}


int Socket::bind(const std::string &ip, int port) {
    int len = sizeof my_adr;
    int i = 1;

    setsockopt(sck, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));

    my_adr.sin_port = htons(port);
    my_adr.sin_addr.s_addr = inet_addr(ip.c_str());
    my_port = port;

    return ::bind(sck, (struct sockaddr *) &my_adr, len);
}


void Socket::stopSsl()
{
#ifdef NETDEBUG
    std::cout << thread_id << "ssl stopping" << std::endl;
#endif
    if(!isssl) return;

    isssl = false;

    if (ssl != NULL) {
        if (issslserver) {
#ifdef NETDEBUG
            std::cout << thread_id << "this is a server connection" << std::endl;
            if (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) {
                std::cout << thread_id << "SSL_SENT_SHUTDOWN IS SET" << std::endl;
            }
            if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) {
                std::cout << thread_id << "SSL_RECEIVED_SHUTDOWN IS SET" << std::endl;
            }
            std::cout << thread_id << "calling 1st ssl shutdown" << std::endl;
#endif
            if (!SSL_shutdown(ssl)) {
#ifdef NETDEBUG
                std::cout << thread_id << "need to call SSL shutdown again" << std::endl;
                if (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) {
                    std::cout << thread_id << "SSL_SENT_SHUTDOWN IS SET" << std::endl;
                }
                if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) {
                    std::cout << thread_id << "SSL_RECEIVED_SHUTDOWN IS SET" << std::endl;
                }
                std::cout << thread_id << "Discarding extra data from client" << std::endl;
#endif

                shutdown(SSL_get_fd(ssl), SHUT_WR);
                char junk[1024];
                readFromSocket(junk, sizeof(junk), 0, 5);
#ifdef NETDEBUG
                std::cout << thread_id << "done" << std::endl;
#endif
            }
        } else {
#ifdef NETDEBUG
            std::cout << thread_id << "this is a client connection" << std::endl;
            if (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) {
                std::cout << thread_id << "SSL_SENT_SHUTDOWN IS SET" << std::endl;
            }
            if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) {
                std::cout << thread_id << "SSL_RECEIVED_SHUTDOWN IS SET" << std::endl;
            }
            std::cout << thread_id << "calling ssl shutdown" << std::endl;
#endif
            SSL_shutdown(ssl);
#ifdef NETDEBUG
            std::cout << thread_id << "done" << std::endl;
#endif
        }
    }

    cleanSsl();

}


int Socket::getPeerSourcePort() {
    return ntohs(peer_adr.sin_port);
}


void Socket::cleanSsl() {  // called when failure in ssl set up functions and from stopSsl
    if (ssl != NULL) {
        SSL_free(ssl);
        ssl = NULL;
    }
    if (ctx != NULL ) {
        SSL_CTX_free(ctx);
        ctx = NULL;
    }
    issslserver = false;
    isssl = false;
}


bool Socket::isSslServer()
{
    return issslserver;
}


Socket *Socket::accept() {
    peer_adr_length = sizeof(struct sockaddr_in);
    s_errno = 0;
    errno = 0;
//    int newfd = this->baseAccept((struct sockaddr *)&peer_adr, &peer_adr_length);
    int newfd = ::accept(sck, (struct sockaddr *) &peer_adr, &peer_adr_length);

    if (newfd > 0) {
        Socket *s = new Socket(newfd, my_adr, peer_adr);
        s->setPort(my_port);
        return s;
    } else {
        s_errno = errno;
        return NULL;
    }
}


bool FunctionLibraryDefinition::Contains(const string& func) const {
  tf_shared_lock l(mu_);
  return function_defs_.find(func) != function_defs_.end();
}

Eliminate `CHECK`-fail from `function.cc`.

PiperOrigin-RevId: 409414744
Change-Id: Ic854e12ab2edb88b165d32e2d632c4ee654d71ad