예제 #1
0
struct result
selection_add_item(struct selection *selection,
                   char const *description,
                   selection_action_fn *action)
{
    if (!selection) return result_set_system_error(EINVAL);
    if (!description) return result_set_system_error(EINVAL);
    if (!description[0]) return result_set_system_error(EINVAL);
        
    int index = selection->items_count;
    int null_index = index + 1;
    ++selection->items_count;
    selection->items = reallocarray_or_die(selection->items,
                                           selection->items_count + 1,
                                           sizeof(ITEM *));
    
    char name[] = "1";
    name[0] += index;
    char *name_dup = strdup_or_die(name);
    char *description_dup = strdup_or_die(description);
    selection->items[index] = new_item(name_dup, description_dup);
    if (!selection->items[index]) {
        free_or_die(description_dup);
        free_or_die(name_dup);
        return result_system_error();
    }
    
    struct selection_item *selection_item = calloc_or_die(1, sizeof(struct selection_item));
    selection_item->action = action;
    set_item_userptr(selection->items[index], selection_item);
    
    selection->items[null_index] = NULL;
    
    return result_success();
}
예제 #2
0
/* add a bundle_result to the results list */
static void add_bundle_file_result(char *bundlename, char *filename, double score)
{
	struct bundle_result *bundle = NULL;
	struct file_result *file;
	struct list *ptr;

	ptr = results;
	while (ptr) {
		struct bundle_result *item;

		item = ptr->data;
		if (strcmp(item->bundle_name, bundlename) == 0) {
			bundle = item;
			break;
		}
		ptr = ptr->next;
	}
	if (!bundle) {
		bundle = calloc(sizeof(struct bundle_result), 1);
		ON_NULL_ABORT(bundle);
		results = list_append_data(results, bundle);
		strncpy(bundle->bundle_name, bundlename, BUNDLE_NAME_MAXLEN - 1);
		/* record if the bundle is tracked on the system */
		bundle->is_tracked = is_tracked_bundle(bundlename);
	}

	file = calloc(sizeof(struct file_result), 1);
	ON_NULL_ABORT(file);
	file->filename = strdup_or_die(filename);
	bundle->files = list_append_data(bundle->files, file);
	file->score = score;
	bundle->score += score;
}
예제 #3
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  parse_forwarder_address
 *
 * Description :  Parse out the host and port from a forwarder address.
 *
 * Parameters  :
 *          1  :  address = The forwarder address to parse.
 *          2  :  hostname = Used to return the hostname. NULL on error.
 *          3  :  port = Used to return the port. Untouched if no port
 *                       is specified.
 *
 * Returns     :  JB_ERR_OK on success
 *                JB_ERR_MEMORY on out of memory
 *                JB_ERR_PARSE on malformed address.
 *
 *********************************************************************/
jb_err parse_forwarder_address(char *address, char **hostname, int *port)
{
   char *p = address;

   if ((*address == '[') && (NULL == strchr(address, ']')))
   {
      /* XXX: Should do some more validity checks here. */
      return JB_ERR_PARSE;
   }

   *hostname = strdup_or_die(address);

   if ((**hostname == '[') && (NULL != (p = strchr(*hostname, ']'))))
   {
      *p++ = '\0';
      memmove(*hostname, (*hostname + 1), (size_t)(p - *hostname));
      if (*p == ':')
      {
         *port = (int)strtol(++p, NULL, 0);
      }
   }
   else if (NULL != (p = strchr(*hostname, ':')))
   {
      *p++ = '\0';
      *port = (int)strtol(p, NULL, 0);
   }

   return JB_ERR_OK;

}
예제 #4
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  compile_host_pattern
 *
 * Description :  Parses and "compiles" an old-school host pattern.
 *
 * Parameters  :
 *          1  :  url = Target pattern_spec to be filled in.
 *          2  :  host_pattern = Host pattern to parse.
 *
 * Returns     :  JB_ERR_OK - Success
 *                JB_ERR_PARSE - Cannot parse regex
 *
 *********************************************************************/
static jb_err compile_host_pattern(struct pattern_spec *url, const char *host_pattern)
{
   char *v[150];
   size_t size;
   char *p;

   /*
    * Parse domain part
    */
   if (host_pattern[strlen(host_pattern) - 1] == '.')
   {
      url->pattern.url_spec.unanchored |= ANCHOR_RIGHT;
   }
   if (host_pattern[0] == '.')
   {
      url->pattern.url_spec.unanchored |= ANCHOR_LEFT;
   }

   /*
    * Split domain into components
    */
   url->pattern.url_spec.dbuffer = strdup_or_die(host_pattern);

   /*
    * Map to lower case
    */
   for (p = url->pattern.url_spec.dbuffer; *p ; p++)
   {
      *p = (char)privoxy_tolower(*p);
   }

   /*
    * Split the domain name into components
    */
   url->pattern.url_spec.dcount = ssplit(url->pattern.url_spec.dbuffer, ".", v, SZ(v));

   if (url->pattern.url_spec.dcount < 0)
   {
      free_pattern_spec(url);
      return JB_ERR_PARSE;
   }
   else if (url->pattern.url_spec.dcount != 0)
   {
      /*
       * Save a copy of the pointers in dvec
       */
      size = (size_t)url->pattern.url_spec.dcount * sizeof(*url->pattern.url_spec.dvec);

      url->pattern.url_spec.dvec = malloc_or_die(size);

      memcpy(url->pattern.url_spec.dvec, v, size);
   }
   /*
    * else dcount == 0 in which case we needn't do anything,
    * since dvec will never be accessed and the pattern will
    * match all domains.
    */
   return JB_ERR_OK;
}
예제 #5
0
char *
area_alloc_description(struct area const *area)
{
    int width = area->box.size.width * 10;
    int length = area->box.size.length * 10;
    int level = area->box.origin.z;
    char *description;
    switch (area->type) {
        case area_type_chamber:
            description = str_alloc_formatted("%u' x %u' chamber", width, length);
            break;
        case area_type_intersection:
            description = strdup_or_die("intersection");
            break;
        case area_type_passage: {
            enum orientation orientation = orientation_from_direction(area->direction);
            if (orientation_east_to_west == orientation) swap(&width, &length);
            description = str_alloc_formatted("%u' passage %s", length,
                                              orientation_name(orientation));
            break;
        }
        case area_type_room:
            description = str_alloc_formatted("%u' x %u' room", width, length);
            break;
        case area_type_stairs_down:
            description = alloc_stairs_description(level + 1, area_type_stairs_down);
            break;
        case area_type_stairs_up:
            description = alloc_stairs_description(level - 1, area_type_stairs_up);
            break;
        default:
            description = str_alloc_formatted("%u' x %u' area", width, length);
            break;
    }
    if (!area->features) return description;
    
    if (area->features & area_features_chimney_up) {
        str_realloc_append_formatted(&description, ", chimney up to ");
        realloc_append_level_description(&description, level - 1);
    }
    
    if (area->features & area_features_chimney_down) {
        str_realloc_append_formatted(&description, ", chimney down to ");
        realloc_append_level_description(&description, level + 1);
    }
    
    if (area->features & area_features_chute_entrance) {
        str_realloc_append_formatted(&description, ", chute down to ");
        realloc_append_level_description(&description, level + 1);
    }
    
    if (area->features & area_features_chute_exit) {
        str_realloc_append_formatted(&description, ", chute down from ");
        realloc_append_level_description(&description, level - 1);
    }
    
    return description;
}
예제 #6
0
struct selection *
selection_alloc_or_die(char const *title)
{
    assert(title && title[0]);
    
    struct selection *selection = calloc_or_die(1, sizeof(struct selection));
    selection->index = -1;
    selection->items = calloc_or_die(1, sizeof(ITEM *));
    selection->title = strdup_or_die(title);
    return selection;
}
예제 #7
0
파일: id_table.c 프로젝트: dzruyk/crypti
/* ID_ITEM FUNCTIONS*/
id_item_t *
id_item_new(char *name)
{
    id_item_t * item;

    item = xmalloc(sizeof(*item));
    memset(item, 0, sizeof(*item));
    item->name = strdup_or_die(name);
    item->type = ID_UNKNOWN;
    item->destructor = id_item_default_release;

    return item;
}
예제 #8
0
struct options *
options_alloc(int argc, char *argv[])
{
    struct options *options = calloc_or_die(1, sizeof(struct options));
    options->command_name = strdup_or_die(basename(argv[0]));
    options->rnd = rnd_alloc();
    
    int action_index = get_options(options, argc, argv);
    if (options->error) return options;
    
    get_action(options, argc, argv, action_index);
    return options;
}
예제 #9
0
static struct hash_ctx *
hash_ctx_new(char *name, int type, void *ctx)
{
	struct hash_ctx *item;

	item = xmalloc(sizeof(*item));
	memset(item, 0, sizeof(*item));

	item->name = strdup_or_die(name);
	item->type = type;
	item->ctx = ctx;

	return item;
}
예제 #10
0
static struct manifest *alloc_manifest(int version, char *component)
{
	struct manifest *manifest;

	manifest = calloc(1, sizeof(struct manifest));
	if (manifest == NULL) {
		abort();
	}

	manifest->version = version;
	manifest->component = strdup_or_die(component);

	return manifest;
}
예제 #11
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  create_pattern_spec
 *
 * Description :  Creates a "pattern_spec" structure from a string.
 *                When finished, free with free_pattern_spec().
 *
 * Parameters  :
 *          1  :  pattern = Target pattern_spec to be filled in.
 *                          Will be zeroed before use.
 *          2  :  buf = Source pattern, null terminated.  NOTE: The
 *                      contents of this buffer are destroyed by this
 *                      function.  If this function succeeds, the
 *                      buffer is copied to pattern->spec.  If this
 *                      function fails, the contents of the buffer
 *                      are lost forever.
 *
 * Returns     :  JB_ERR_OK - Success
 *                JB_ERR_PARSE - Cannot parse regex (Detailed message
 *                               written to system log)
 *
 *********************************************************************/
jb_err create_pattern_spec(struct pattern_spec *pattern, char *buf)
{
   static const struct
   {
      /** The tag pattern prefix to match */
      const char *prefix;

      /** The length of the prefix to match */
      const size_t prefix_length;

      /** The pattern flag */
      const unsigned flag;
   } tag_pattern[] = {
      { "TAG:",              4, PATTERN_SPEC_TAG_PATTERN},
      { "NO-REQUEST-TAG:",  15, PATTERN_SPEC_NO_REQUEST_TAG_PATTERN},
      { "NO-RESPONSE-TAG:", 16, PATTERN_SPEC_NO_RESPONSE_TAG_PATTERN}
   };
   int i;

   assert(pattern);
   assert(buf);

   memset(pattern, '\0', sizeof(*pattern));

   /* Remember the original specification for the CGI pages. */
   pattern->spec = strdup_or_die(buf);

   /* Check if it's a tag pattern */
   for (i = 0; i < SZ(tag_pattern); i++)
   {
      if (0 == strncmpic(pattern->spec, tag_pattern[i].prefix, tag_pattern[i].prefix_length))
      {
         /* The regex starts after the prefix */
         const char *tag_regex = buf + tag_pattern[i].prefix_length;

         pattern->flags |= tag_pattern[i].flag;

         return compile_pattern(tag_regex, NO_ANCHORING, pattern,
            &pattern->pattern.tag_regex);
      }
   }

   /* If it isn't a tag pattern it must be an URL pattern. */
   pattern->flags |= PATTERN_SPEC_URL_PATTERN;

   return compile_url_pattern(pattern, buf);

}
예제 #12
0
// expects filename w/o path_prefix prepended
bool is_under_mounted_directory(const char *filename)
{
	bool ret = false;
	int err;
	char *token;
	char *mountpoint;
	char *dir;
	char *fname;
	char *tmp;

	if (mounted_dirs == NULL) {
		return false;
	}

	dir = strdup_or_die(mounted_dirs);

	token = strtok(dir + 1, ":");
	while (token != NULL) {
		string_or_die(&mountpoint, "%s/", token);

		tmp = mk_full_filename(path_prefix, filename);
		string_or_die(&fname, ":%s:", tmp);
		free_string(&tmp);

		err = strncmp(fname, mountpoint, strlen(mountpoint));
		free_string(&fname);
		if (err == 0) {
			free_string(&mountpoint);
			ret = true;
			break;
		}

		token = strtok(NULL, ":");

		free_string(&mountpoint);
	}

	free_string(&dir);

	return ret;
}
예제 #13
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  init_domain_components
 *
 * Description :  Splits the domain name so we can compare it
 *                against wildcards. It used to be part of
 *                parse_http_url, but was separated because the
 *                same code is required in chat in case of
 *                intercepted requests.
 *
 * Parameters  :
 *          1  :  http = pointer to the http structure to hold elements.
 *
 * Returns     :  JB_ERR_OK on success
 *                JB_ERR_PARSE on malformed command/URL
 *                             or >100 domains deep.
 *
 *********************************************************************/
jb_err init_domain_components(struct http_request *http)
{
   char *vec[BUFFER_SIZE];
   size_t size;
   char *p;

   http->dbuffer = strdup_or_die(http->host);

   /* map to lower case */
   for (p = http->dbuffer; *p ; p++)
   {
      *p = (char)privoxy_tolower(*p);
   }

   /* split the domain name into components */
   http->dcount = ssplit(http->dbuffer, ".", vec, SZ(vec));

   if (http->dcount <= 0)
   {
      /*
       * Error: More than SZ(vec) components in domain
       *    or: no components in domain
       */
      log_error(LOG_LEVEL_ERROR, "More than SZ(vec) components in domain or none at all.");
      return JB_ERR_PARSE;
   }

   /* save a copy of the pointers in dvec */
   size = (size_t)http->dcount * sizeof(*http->dvec);

   http->dvec = malloc_or_die(size);

   memcpy(http->dvec, vec, size);

   return JB_ERR_OK;
}
예제 #14
0
파일: pcopy.c 프로젝트: brkorb/pcopy
/**
 * Make a destination name.  If the destination option is not provided,
 * the destination is the base name of the input file.  If the destination
 * is a directory, the base name of the source is always appended.
 * If the destination is a file, then make certain we only copy one file
 * to this destination.
 *
 * @param[in] sname  source file name.
 * @returns   the name of the corresponding output file.
 */
static inline char const *
make_dest_name(char const * sname)
{
    static char const no_dir[] =
        "no directory component in source name '%s'\n"
        "and no destination directory was specified.\n";

    if (! HAVE_OPT(DESTINATION)) {
        char const * p = strrchr(sname, '/');
        if (p == NULL)
            usage_message(no_dir, sname);
        unlink(++p);
        return strdup_or_die(p);
    }

    {
        static bool been_here = false;
        char const * dname = OPT_ARG(DESTINATION);
        struct stat sb;

        if (stat(dname, &sb) == 0) {
            if (S_ISDIR(sb.st_mode)) {
                char const * bn = strrchr(sname, '/');
                char * p;
                bn = bn ? (bn + 1) : sname;
                p  = malloc(strlen(dname) + strlen(bn) + 2);
                if (p == NULL)
                    fserr(PCOPY_EXIT_NO_MEM, "allocating", "destination name");
                sprintf(p, "%s/%s", dname, bn);
                unlink(p);
                return p;
            }
            if (S_ISREG(sb.st_mode)) {
                if (been_here)
                    die(PCOPY_EXIT_BAD_CONFIG,
                        "destination for multiple sources is one file");
                been_here = true;
                unlink(dname);
                return strdup_or_die(dname);
            }
            errno = EINVAL;
            fserr(PCOPY_EXIT_BAD_CONFIG,
                  "invalid destination fs type (not dir or file)", dname);
        }

        /*
         * We could not stat "dname".  It must be a file name.  Make sure any
         * directory part exists.  If we call this routine again, we should
         * successfully stat our destination file and trip over the
         * "been_here" flag.
         */
        dname = strdup_or_die(dname);
        been_here = true;
        {
            static char const bad_stat[] = "stat-ing dir portion";
            char * p = strrchr(dname, '/');
            if (p == NULL) {
                /*
                 * No directory part.  Destination file is for current dir.
                 */
                return dname;
            }

            *p = NUL;
            if (stat(dname, &sb) != 0)
                fserr(PCOPY_EXIT_BAD_CONFIG, bad_stat, OPT_ARG(DESTINATION));
            if (! S_ISDIR(sb.st_mode)) {
                errno = ENOTDIR;
                fserr(PCOPY_EXIT_BAD_CONFIG, bad_stat, OPT_ARG(DESTINATION));
            }
            *p = '/';
        }

        return dname;
    }
}
예제 #15
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  compile_url_pattern
 *
 * Description :  Compiles the three parts of an URL pattern.
 *
 * Parameters  :
 *          1  :  url = Target pattern_spec to be filled in.
 *          2  :  buf = The url pattern to compile. Will be messed up.
 *
 * Returns     :  JB_ERR_OK - Success
 *                JB_ERR_MEMORY - Out of memory
 *                JB_ERR_PARSE - Cannot parse regex
 *
 *********************************************************************/
static jb_err compile_url_pattern(struct pattern_spec *url, char *buf)
{
   char *p;

   p = strchr(buf, '/');
   if (NULL != p)
   {
      /*
       * Only compile the regex if it consists of more than
       * a single slash, otherwise it wouldn't affect the result.
       */
      if (p[1] != '\0')
      {
         /*
          * XXX: does it make sense to compile the slash at the beginning?
          */
         jb_err err = compile_pattern(p, LEFT_ANCHORED, url, &url->pattern.url_spec.preg);

         if (JB_ERR_OK != err)
         {
            return err;
         }
      }
      *p = '\0';
   }

   /*
    * IPv6 numeric hostnames can contain colons, thus we need
    * to delimit the hostname before the real port separator.
    * As brackets are already used in the hostname pattern,
    * we use angle brackets ('<', '>') instead.
    */
   if ((buf[0] == '<') && (NULL != (p = strchr(buf + 1, '>'))))
   {
      *p++ = '\0';
      buf++;

      if (*p == '\0')
      {
         /* IPv6 address without port number */
         p = NULL;
      }
      else if (*p != ':')
      {
         /* Garbage after address delimiter */
         return JB_ERR_PARSE;
      }
   }
   else
   {
      p = strchr(buf, ':');
   }

   if (NULL != p)
   {
      *p++ = '\0';
      url->pattern.url_spec.port_list = strdup_or_die(p);
   }
   else
   {
      url->pattern.url_spec.port_list = NULL;
   }

   if (buf[0] != '\0')
   {
      return compile_host_pattern(url, buf);
   }

   return JB_ERR_OK;

}
예제 #16
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  parse_http_request
 *
 * Description :  Parse out the host and port from the URL.  Find the
 *                hostname & path, port (if ':'), and/or password (if '@')
 *
 * Parameters  :
 *          1  :  req = HTTP request line to break down
 *          2  :  http = pointer to the http structure to hold elements
 *
 * Returns     :  JB_ERR_OK on success
 *                JB_ERR_CGI_PARAMS on malformed command/URL
 *                                  or >100 domains deep.
 *
 *********************************************************************/
jb_err parse_http_request(const char *req, struct http_request *http)
{
   char *buf;
   char *v[3];
   int n;
   jb_err err;

   memset(http, '\0', sizeof(*http));

   buf = strdup_or_die(req);

   n = ssplit(buf, " \r\n", v, SZ(v));
   if (n != 3)
   {
      freez(buf);
      return JB_ERR_PARSE;
   }

   /*
    * Fail in case of unknown methods
    * which we might not handle correctly.
    *
    * XXX: There should be a config option
    * to forward requests with unknown methods
    * anyway. Most of them don't need special
    * steps.
    */
   if (unknown_method(v[0]))
   {
      log_error(LOG_LEVEL_ERROR, "Unknown HTTP method detected: %s", v[0]);
      freez(buf);
      return JB_ERR_PARSE;
   }

   if (JB_ERR_OK != normalize_http_version(v[2]))
   {
      freez(buf);
      return JB_ERR_PARSE;
   }

   http->ssl = !strcmpic(v[0], "CONNECT");

   err = parse_http_url(v[1], http, !http->ssl);
   if (err)
   {
      freez(buf);
      return err;
   }

   /*
    * Copy the details into the structure
    */
   http->cmd = strdup_or_die(req);
   http->gpc = strdup_or_die(v[0]);
   http->ver = strdup_or_die(v[2]);
   http->ocmd = strdup_or_die(http->cmd);

   freez(buf);

   return JB_ERR_OK;

}
예제 #17
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  parse_http_url
 *
 * Description :  Parse out the host and port from the URL.  Find the
 *                hostname & path, port (if ':'), and/or password (if '@')
 *
 * Parameters  :
 *          1  :  url = URL (or is it URI?) to break down
 *          2  :  http = pointer to the http structure to hold elements.
 *                       Must be initialized with valid values (like NULLs).
 *          3  :  require_protocol = Whether or not URLs without
 *                                   protocol are acceptable.
 *
 * Returns     :  JB_ERR_OK on success
 *                JB_ERR_PARSE on malformed command/URL
 *                             or >100 domains deep.
 *
 *********************************************************************/
jb_err parse_http_url(const char *url, struct http_request *http, int require_protocol)
{
   int host_available = 1; /* A proxy can dream. */

   /*
    * Save our initial URL
    */
   http->url = strdup_or_die(url);

   /*
    * Check for * URI. If found, we're done.
    */
   if (*http->url == '*')
   {
      http->path = strdup_or_die("*");
      http->hostport = strdup_or_die("");
      if (http->url[1] != '\0')
      {
         return JB_ERR_PARSE;
      }
      return JB_ERR_OK;
   }


   /*
    * Split URL into protocol,hostport,path.
    */
   {
      char *buf;
      char *url_noproto;
      char *url_path;

      buf = strdup_or_die(url);

      /* Find the start of the URL in our scratch space */
      url_noproto = buf;
      if (strncmpic(url_noproto, "http://",  7) == 0)
      {
         url_noproto += 7;
      }
      else if (strncmpic(url_noproto, "https://", 8) == 0)
      {
         /*
          * Should only happen when called from cgi_show_url_info().
          */
         url_noproto += 8;
         http->ssl = 1;
      }
      else if (*url_noproto == '/')
      {
        /*
         * Short request line without protocol and host.
         * Most likely because the client's request
         * was intercepted and redirected into Privoxy.
         */
         http->host = NULL;
         host_available = 0;
      }
      else if (require_protocol)
      {
         freez(buf);
         return JB_ERR_PARSE;
      }

      url_path = strchr(url_noproto, '/');
      if (url_path != NULL)
      {
         /*
          * Got a path.
          *
          * NOTE: The following line ignores the path for HTTPS URLS.
          * This means that you get consistent behaviour if you type a
          * https URL in and it's parsed by the function.  (When the
          * URL is actually retrieved, SSL hides the path part).
          */
         http->path = strdup_or_die(http->ssl ? "/" : url_path);
         *url_path = '\0';
         http->hostport = strdup_or_die(url_noproto);
      }
      else
      {
         /*
          * Repair broken HTTP requests that don't contain a path,
          * or CONNECT requests
          */
         http->path = strdup_or_die("/");
         http->hostport = strdup_or_die(url_noproto);
      }

      freez(buf);
   }

   if (!host_available)
   {
      /* Without host, there is nothing left to do here */
      return JB_ERR_OK;
   }

   /*
    * Split hostport into user/password (ignored), host, port.
    */
   {
      char *buf;
      char *host;
      char *port;

      buf = strdup_or_die(http->hostport);

      /* check if url contains username and/or password */
      host = strchr(buf, '@');
      if (host != NULL)
      {
         /* Contains username/password, skip it and the @ sign. */
         host++;
      }
      else
      {
         /* No username or password. */
         host = buf;
      }

      /* Move after hostname before port number */
      if (*host == '[')
      {
         /* Numeric IPv6 address delimited by brackets */
         host++;
         port = strchr(host, ']');

         if (port == NULL)
         {
            /* Missing closing bracket */
            freez(buf);
            return JB_ERR_PARSE;
         }

         *port++ = '\0';

         if (*port == '\0')
         {
            port = NULL;
         }
         else if (*port != ':')
         {
            /* Garbage after closing bracket */
            freez(buf);
            return JB_ERR_PARSE;
         }
      }
      else
      {
         /* Plain non-escaped hostname */
         port = strchr(host, ':');
      }

      /* check if url contains port */
      if (port != NULL)
      {
         /* Contains port */
         char *endptr;
         long parsed_port;
         /* Terminate hostname and point to start of port string */
         *port++ = '\0';
         parsed_port = strtol(port, &endptr, 10);
         if ((parsed_port <= 0) || (parsed_port > 65535) || (*endptr != '\0'))
         {
            log_error(LOG_LEVEL_ERROR, "Invalid port in URL: %s.", url);
            freez(buf);
            return JB_ERR_PARSE;
         }
         http->port = (int)parsed_port;
      }
      else
      {
         /* No port specified. */
         http->port = (http->ssl ? 443 : 80);
      }

      http->host = strdup_or_die(host);

      freez(buf);
   }

#ifdef FEATURE_EXTENDED_HOST_PATTERNS
   return JB_ERR_OK;
#else
   /* Split domain name so we can compare it against wildcards */
   return init_domain_components(http);
#endif /* def FEATURE_EXTENDED_HOST_PATTERNS */

}
예제 #18
0
파일: urlmatch.c 프로젝트: 11liju/Potatso
/*********************************************************************
 *
 * Function    :  match_portlist
 *
 * Description :  Check if a given number is covered by a comma
 *                separated list of numbers and ranges (a,b-c,d,..)
 *
 * Parameters  :
 *          1  :  portlist = String with list
 *          2  :  port = port to check
 *
 * Returns     :  0 => no match
 *                1 => match
 *
 *********************************************************************/
int match_portlist(const char *portlist, int port)
{
   char *min, *max, *next, *portlist_copy;

   min = portlist_copy = strdup_or_die(portlist);

   /*
    * Zero-terminate first item and remember offset for next
    */
   if (NULL != (next = strchr(portlist_copy, (int) ',')))
   {
      *next++ = '\0';
   }

   /*
    * Loop through all items, checking for match
    */
   while (NULL != min)
   {
      if (NULL == (max = strchr(min, (int) '-')))
      {
         /*
          * No dash, check for equality
          */
         if (port == atoi(min))
         {
            freez(portlist_copy);
            return(1);
         }
      }
      else
      {
         /*
          * This is a range, so check if between min and max,
          * or, if max was omitted, between min and 65K
          */
         *max++ = '\0';
         if (port >= atoi(min) && port <= (atoi(max) ? atoi(max) : 65535))
         {
            freez(portlist_copy);
            return(1);
         }

      }

      /*
       * Jump to next item
       */
      min = next;

      /*
       * Zero-terminate next item and remember offset for n+1
       */
      if ((NULL != next) && (NULL != (next = strchr(next, (int) ','))))
      {
         *next++ = '\0';
      }
   }

   freez(portlist_copy);
   return 0;

}
예제 #19
0
/* This function is meant to be called while staging file to fix any missing/incorrect paths.
 * While staging a file, if its parent directory is missing, this would try to create the path
 * by breaking it into sub-paths and fixing them top down.
 * Here, target_MoM is the consolidated manifest for the version you are trying to update/verify.
 */
int verify_fix_path(char *targetpath, struct manifest *target_MoM)
{
	struct list *path_list = NULL; /* path_list contains the subparts in the path */
	char *path;
	char *tmp = NULL, *target = NULL;
	char *url = NULL;
	struct stat sb;
	int ret = 0;
	struct file *file;
	char *tar_dotfile = NULL;
	struct list *list1 = NULL;

	/* This shouldn't happen */
	if (strcmp(targetpath, "/") == 0) {
		return ret;
	}

	/* Removing trailing '/' from the path */
	path = strdup_or_die(targetpath);
	if (path[strlen(path) - 1] == '/') {
		path[strlen(path) - 1] = '\0';
	}

	/* Breaking down the path into parts.
	 * eg. Path /usr/bin/foo will be broken into /usr,/usr/bin and /usr/bin/foo
	 */
	while (strcmp(path, "/") != 0) {
		path_list = list_prepend_data(path_list, strdup_or_die(path));
		tmp = strdup_or_die(dirname(path));
		free_string(&path);
		path = tmp;
	}
	free_string(&path);

	list1 = list_head(path_list);
	while (list1) {
		path = list1->data;
		list1 = list1->next;

		free_string(&target);
		free_string(&tar_dotfile);
		free_string(&url);

		target = mk_full_filename(path_prefix, path);

		/* Search for the file in the manifest, to get the hash for the file */
		file = search_file_in_manifest(target_MoM, path);
		if (file == NULL) {
			fprintf(stderr, "Error: Path %s not found in any of the subscribed manifests"
					"in verify_fix_path for path_prefix %s\n",
				path, path_prefix);
			ret = -1;
			goto end;
		}

		if (file->is_deleted) {
			fprintf(stderr, "Error: Path %s found deleted in verify_fix_path\n", path);
			ret = -1;
			goto end;
		}

		ret = stat(target, &sb);
		if (ret == 0) {
			if (verify_file(file, target)) {
				continue;
			}
			fprintf(stderr, "Hash did not match for path : %s ... fixing\n", path);
		} else if (ret == -1 && errno == ENOENT) {
			fprintf(stderr, "Path %s is missing on the file system ... fixing\n", path);
		} else {
			goto end;
		}
		/* In some circumstances (Docker using layers between updates/bundle adds,
                 * corrupt staging content) we could have content which fails to stage.
		 * In order to avoid this causing failure in verify_fix_path, remove the
		 * staging content before proceeding. This also cleans up in case any prior
		 * download failed in a partial state.
		 */
		unlink_all_staged_content(file);

		string_or_die(&tar_dotfile, "%s/download/.%s.tar", state_dir, file->hash);

		string_or_die(&url, "%s/%i/files/%s.tar", content_url, file->last_change, file->hash);
		ret = swupd_curl_get_file(url, tar_dotfile);

		if (ret != 0) {
			fprintf(stderr, "Error: Failed to download file %s in verify_fix_path\n", file->filename);
			unlink(tar_dotfile);
			goto end;
		}
		if (untar_full_download(file) != 0) {
			fprintf(stderr, "Error: Failed to untar file %s\n", file->filename);
			ret = -1;
			goto end;
		}

		ret = do_staging(file, target_MoM);
		if (ret != 0) {
			fprintf(stderr, "Error: Path %s failed to stage in verify_fix_path\n", path);
			goto end;
		}
	}
end:
	free_string(&target);
	free_string(&tar_dotfile);
	free_string(&url);
	list_free_list_and_data(path_list, free_path_data);
	return ret;
}
예제 #20
0
static struct manifest *manifest_from_file(int version, char *component, bool header_only, bool latest, bool is_mix)
{
	FILE *infile;
	char line[MANIFEST_LINE_MAXLEN], *c, *c2;
	int count = 0;
	int deleted = 0;
	struct manifest *manifest;
	struct list *includes = NULL;
	char *filename;
	char *basedir;
	uint64_t filecount = 0;
	uint64_t contentsize = 0;
	int manifest_hdr_version;
	int manifest_enc_version;

	if (!is_mix) {
		basedir = state_dir;
	} else {
		basedir = MIX_STATE_DIR;
	}
	string_or_die(&filename, "%s/%i/Manifest.%s", basedir, version, component);

	infile = fopen(filename, "rbm");
	if (infile == NULL) {
		free_string(&filename);
		return NULL;
	}
	free_string(&filename);

	/* line 1: MANIFEST\t<version> */
	line[0] = 0;
	if (fgets(line, MANIFEST_LINE_MAXLEN - 1, infile) == NULL) {
		goto err_close;
	}

	if (strncmp(line, "MANIFEST\t", 9) != 0) {
		goto err_close;
	}

	c = &line[9];
	manifest_enc_version = strtoull(c, NULL, 10);
	if (manifest_enc_version == 0) {
		goto err_close;
	}

	line[0] = 0;
	while (strcmp(line, "\n") != 0) {
		/* read the header */
		line[0] = 0;
		if (fgets(line, MANIFEST_LINE_MAXLEN - 1, infile) == NULL) {
			break;
		}
		c = strchr(line, '\n');
		if (c) {
			*c = 0;
		} else {
			goto err_close;
		}

		if (strlen(line) == 0) {
			break;
		}
		c = strchr(line, '\t');
		if (c) {
			c++;
		} else {
			goto err_close;
		}

		if (strncmp(line, "version:", 8) == 0) {
			manifest_hdr_version = strtoull(c, NULL, 10);
			if (manifest_hdr_version != version) {
				goto err_close;
			}
		}
		if (strncmp(line, "filecount:", 10) == 0) {
			filecount = strtoull(c, NULL, 10);
			if (filecount > 4000000) {
				/* Note on the number 4,000,000. We want this
				 * to be big enough to allow Manifest.Full to
				 * pass (currently about 450,000 March 18) but
				 * small enough that when multiplied by
				 * sizeof(struct file) it fits into
				 * size_t. For a system with size_t being a 32
				 * bit value, this constrains it to be less
				 * than about 6,000,000, but close to infinity
				 * for systems with 64 bit size_t.
				 */
				fprintf(stderr, "Error: preposterous (%" PRIu64 ") number of files in %s Manifest, more than 4 million skipping\n",
					filecount, component);
				goto err_close;
			}
		}
		if (strncmp(line, "contentsize:", 12) == 0) {
			contentsize = strtoull(c, NULL, 10);
			if (contentsize > 2000000000000UL) {
				fprintf(stderr, "Error: preposterous (%" PRIu64 ") size of files in %s Manifest, more than 2TB skipping\n",
					contentsize, component);
				goto err_close;
			}
		}
		if (latest && strncmp(component, "MoM", 3) == 0) {
			if (strncmp(line, "actions:", 8) == 0) {
				post_update_actions = list_prepend_data(post_update_actions, strdup_or_die(c));
				if (!post_update_actions->data) {
					fprintf(stderr, "WARNING: Unable to read post update action from Manifest.MoM. \
							Another update or verify may be required.\n");
				}
			}
		}
예제 #21
0
/* Consider adding a remove_leftovers() that runs in verify/fix in order to
 * allow this function to mkdtemp create folders for parallel build */
enum swupd_code do_staging(struct file *file, struct manifest *MoM)
{
	char *statfile = NULL, *tmp = NULL, *tmp2 = NULL;
	char *dir, *base, *rel_dir;
	char *tarcommand = NULL;
	char *original = NULL;
	char *target = NULL;
	char *targetpath = NULL;
	char *rename_target = NULL;
	char *rename_tmpdir = NULL;
	char real_path[4096] = { 0 };
	int ret;
	struct stat s;

	tmp = strdup_or_die(file->filename);
	tmp2 = strdup_or_die(file->filename);

	dir = dirname(tmp);
	base = basename(tmp2);

	rel_dir = dir;
	if (*dir == '/') {
		rel_dir = dir + 1;
	}

	string_or_die(&original, "%s/staged/%s", state_dir, file->hash);

	string_or_die(&targetpath, "%s%s", path_prefix, rel_dir);
	ret = stat(targetpath, &s);

	if ((ret == -1) && (errno == ENOENT)) {
		if (MoM) {
			warn("Update target directory does not exist: %s. Trying to fix it\n", targetpath);
			verify_fix_path(dir, MoM);
		} else {
			warn("Update target directory does not exist: %s. Auto-fix disabled\n", targetpath);
		}

	} else if (!S_ISDIR(s.st_mode)) {
		error("Update target exists but is NOT a directory: %s\n", targetpath);
	}
	if (!realpath(targetpath, real_path)) {
		ret = -1;
		goto out;
	} else if (strcmp(path_prefix, targetpath) != 0 &&
		   strcmp(targetpath, real_path) != 0) {
		/*
		 * targetpath and real_path should always be equal but
		 * in the case of the targetpath being the path_prefix
		 * there is a trailing '/' in path_prefix but realpath
		 * doesn't keep the trailing '/' so check for that case
		 * specifically.
		 */
		ret = -1;
		goto out;
	}

	string_or_die(&target, "%s%s/.update.%s", path_prefix, rel_dir, base);
	ret = swupd_rm(target);
	if (ret < 0 && ret != -ENOENT) {
		error("Failed to remove %s\n", target);
	}

	string_or_die(&statfile, "%s%s", path_prefix, file->filename);

	memset(&s, 0, sizeof(struct stat));
	ret = lstat(statfile, &s);
	if (ret == 0) {
		if ((file->is_dir && !S_ISDIR(s.st_mode)) ||
		    (file->is_link && !S_ISLNK(s.st_mode)) ||
		    (file->is_file && !S_ISREG(s.st_mode))) {
			//file type changed, move old out of the way for new
			ret = swupd_rm(statfile);
			if (ret < 0) {
				ret = SWUPD_COULDNT_REMOVE_FILE;
				goto out;
			}
		}
	}
	free_string(&statfile);

	if (file->is_dir || S_ISDIR(s.st_mode)) {
		/* In the btrfs only scenario there was an implicit
		 * "create_or_update_dir()" via un-tar-ing a directory.tar after
		 * download and the untar happens in the staging subvolume which
		 * then gets promoted to a "real" usable subvolume.  But for
		 * a live rootfs the directory needs copied out of staged
		 * and into the rootfs.  Tar is a way to copy with
		 * attributes and it includes internal logic that does the
		 * right thing to overlay a directory onto something
		 * pre-existing: */
		/* In order to avoid tar transforms with directories, rename
		 * the directory before and after the tar command */
		string_or_die(&rename_tmpdir, "%s/tmprenamedir", state_dir);
		ret = create_staging_renamedir(rename_tmpdir);
		if (ret) {
			ret = SWUPD_COULDNT_CREATE_DIR;
			goto out;
		}
		string_or_die(&rename_target, "%s/%s", rename_tmpdir, base);
		if (rename(original, rename_target)) {
			ret = SWUPD_COULDNT_RENAME_DIR;
			goto out;
		}
		string_or_die(&tarcommand, TAR_COMMAND " -C '%s' " TAR_PERM_ATTR_ARGS " -cf - './%s' 2> /dev/null | " TAR_COMMAND " -C '%s%s' " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
			      rename_tmpdir, base, path_prefix, rel_dir);
		ret = system(tarcommand);
		if (ret == -1) {
			ret = SWUPD_SUBPROCESS_ERROR;
		}
		if (WIFEXITED(ret)) {
			ret = WEXITSTATUS(ret);
		}
		free_string(&tarcommand);
		if (rename(rename_target, original)) {
			ret = SWUPD_COULDNT_RENAME_DIR;
			goto out;
		}
		if (ret) {
			ret = SWUPD_COULDNT_RENAME_DIR;
			goto out;
		}
	} else { /* (!file->is_dir && !S_ISDIR(stat.st_mode)) */
		/* can't naively hard link(): Non-read-only files with same hash must remain
		 * separate copies otherwise modifications to one instance of the file
		 * propagate to all instances of the file perhaps causing subtle data corruption from
		 * a user's perspective.  In practice the rootfs is stateless and owned by us.
		 * Additionally cross-mount hardlinks fail and it's hard to know what an admin
		 * might have for overlaid mounts.  The use of tar is a simple way to copy, but
		 * inefficient.  So prefer hardlink and fall back if needed: */
		ret = -1;
		if (!file->is_config && !file->is_state && !file->use_xattrs) {
			ret = link(original, target);
		}
		if (ret < 0) {
			/* either the hardlink failed, or it was undesirable (config), do a tar-tar dance */
			/* In order to avoid tar transforms, rename the file
			 * before and after the tar command */
			string_or_die(&rename_target, "%s/staged/.update.%s", state_dir, base);
			ret = rename(original, rename_target);
			if (ret) {
				ret = SWUPD_COULDNT_RENAME_FILE;
				goto out;
			}
			string_or_die(&tarcommand, TAR_COMMAND " -C '%s/staged' " TAR_PERM_ATTR_ARGS " -cf - '.update.%s' 2> /dev/null | " TAR_COMMAND " -C '%s%s' " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
				      state_dir, base, path_prefix, rel_dir);
			ret = system(tarcommand);
			if (ret == -1) {
				ret = SWUPD_SUBPROCESS_ERROR;
			}
			if (WIFEXITED(ret)) {
				ret = WEXITSTATUS(ret);
			}
			free_string(&tarcommand);
			ret = rename(rename_target, original);
			if (ret) {
				ret = SWUPD_COULDNT_RENAME_FILE;
				goto out;
			}
		}

		struct stat buf;
		int err;

		free_string(&file->staging);
		string_or_die(&file->staging, "%s%s/.update.%s", path_prefix, rel_dir, base);

		err = lstat(file->staging, &buf);
		if (err != 0) {
			free_string(&file->staging);
			ret = SWUPD_COULDNT_CREATE_FILE;
			goto out;
		}
	}

out:
	free_string(&target);
	free_string(&targetpath);
	free_string(&original);
	free_string(&rename_target);
	free_string(&rename_tmpdir);
	free_string(&tmp);
	free_string(&tmp2);

	return ret;
}
예제 #22
0
/* Getaddrinfo implementation */
static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client_state *csp, int is_proxy)
{
    struct addrinfo hints, *result, *rp;
    char service[6];
    int retval;
    jb_socket fd;
    fd_set wfds;
    struct timeval timeout;
    #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
    int   flags;
    #endif
    int connect_failed;
    /*
    * XXX: Initializeing it here is only necessary
    *      because not all situations are properly
    *      covered yet.
    */
    int socket_error = 0;
    if (is_proxy) {
        log_time_stage(csp, TIME_STAGE_PROXY_DNS_START);
    }else {
        log_time_stage(csp, TIME_STAGE_DNS_START);
    }

    struct access_control_addr dst[1];

    /* Don't leak memory when retrying. */
    freez(csp->error_message);
    freez(csp->http->host_ip_addr_str);

    retval = snprintf(service, sizeof(service), "%d", portnum);
    if ((-1 == retval) || (sizeof(service) <= retval))
    {
        log_error(LOG_LEVEL_ERROR,
         "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
         portnum);
        csp->error_message = strdup("Invalid port number");
        csp->http->host_ip_addr_str = strdup("unknown");
        return(JB_INVALID_SOCKET);
    }

    memset((char *)&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV; /* avoid service look-up */
    #ifdef AI_ADDRCONFIG
    hints.ai_flags |= AI_ADDRCONFIG;
    #endif
    if ((retval = getaddrinfo(host, service, &hints, &result)))
    {
        if (is_proxy) {
            log_time_stage(csp, TIME_STAGE_PROXY_DNS_FAIL);
        }else {
            log_time_stage(csp, TIME_STAGE_DNS_FAIL);
            if (proxy_list) {
                csp->routing = ROUTE_PROXY;
                csp->current_forward_stage = FORWARD_STAGE_DNS_FAILURE;
                return connect_to_forward(csp, proxy_list, 1);
            }
        }
        log_error(LOG_LEVEL_INFO,
         "Can not resolve %s: %s", host, gai_strerror(retval));
        /* XXX: Should find a better way to propagate this error. */
        errno = EINVAL;
        csp->error_message = strdup(gai_strerror(retval));
        csp->http->host_ip_addr_str = strdup("unknown");
        return(JB_INVALID_SOCKET);
    }

    csp->http->host_ip_addr_str = malloc_or_die(NI_MAXHOST);

    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        memcpy(&dst->addr, rp->ai_addr, rp->ai_addrlen);
    #ifdef FEATURE_ACL
        if (block_acl(dst, csp))
        {
    #ifdef __OS2__
         socket_error = errno = SOCEPERM;
    #else
         socket_error = errno = EPERM;
    #endif
         continue;
        }
    #endif /* def FEATURE_ACL */

        retval = getnameinfo(rp->ai_addr, rp->ai_addrlen, csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
        if (retval)
        {
         log_error(LOG_LEVEL_ERROR,
            "Failed to get the host name from the socket structure: %s",
            gai_strerror(retval));
         continue;
        }
        if (is_proxy) {
            log_time_stage(csp, TIME_STAGE_PROXY_DNS_END);
        }else {
            csp->http->remote_host_ip_addr_str = strdup_or_die(csp->http->host_ip_addr_str);
            log_time_stage(csp, TIME_STAGE_DNS_END);
        }
        if (!is_proxy) {
            if (dst->addr.ss_family == AF_INET) {
                // IPv4
                if (csp->current_forward_stage == FORWARD_STAGE_NONE && !csp->forward_determined) {
                    log_time_stage(csp, TIME_STAGE_IP_RULE_MATCH_START);
                    struct forward_spec *fwd = forward_ip(csp, dst->addr);
                    log_time_stage(csp, TIME_STAGE_IP_RULE_MATCH_END);
                    csp->forward_determined = 1;
                    if (csp->routing == ROUTE_NONE) {
                        log_time_stage(csp, TIME_STAGE_DNS_IP_RULE_MATCH_START);
                        fwd = forward_dns_pollution_ip(csp, dst->addr);
                        log_time_stage(csp, TIME_STAGE_DNS_IP_RULE_MATCH_END);
                        if (csp->routing == ROUTE_NONE) {
                            if (global_mode && proxy_list) {
                                log_time_stage(csp, TIME_STAGE_GLOBAL_MODE);
                                fwd = proxy_list;
                                csp->routing = ROUTE_PROXY;
                            }else {
                                log_time_stage(csp, TIME_STAGE_NON_GLOBAL_MODE);
                                csp->routing = ROUTE_DIRECT;
                            }
                        }
                    }
                    if (csp->routing == ROUTE_NONE || csp->routing == ROUTE_DIRECT) {

                    }else if (csp->routing == ROUTE_BLOCK) {
                        log_error(LOG_LEVEL_CONNECT,
                                  "Block request to ip: %s", csp->http->host_ip_addr_str);
                        return JB_INVALID_SOCKET;
                    }else {
                        if (fwd) {
                            return connect_to_forward(csp, fwd, 1);
                        }else {
                            // No proxy provided.
                        }
                    }
                }
            }else {
                csp->is_ipv6 = 1;
                csp->routing = ROUTE_DIRECT;
                log_time_stage(csp, TIME_STAGE_IPV6);
            }
        }
        if (is_proxy) {
            log_time_stage(csp, TIME_STAGE_PROXY_START);
        }else {
            log_time_stage(csp, TIME_STAGE_REMOTE_START);
        }

        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    #ifdef _WIN32
        if (fd == JB_INVALID_SOCKET)
    #else
        if (fd < 0)
    #endif
        {
         continue;
        }

    #ifndef _WIN32
        if (fd >= FD_SETSIZE)
        {
            log_error(LOG_LEVEL_ERROR,
            "Server socket number too high to use select(): %d >= %d",
            fd, FD_SETSIZE);
            close_socket(fd);
            freeaddrinfo(result);
            return JB_INVALID_SOCKET;
        }
    #endif

#ifdef FEATURE_EXTERNAL_FILTERS
        mark_socket_for_close_on_execute(fd);
#endif

        set_no_delay_flag(fd);
        int yes = 1;
        setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof (yes));

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
        if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
        {
            flags |= O_NDELAY;
            fcntl(fd, F_SETFL, flags);
        }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

        connect_failed = 0;
        while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET)
        {
#ifdef __OS2__
            errno = sock_errno();
#endif /* __OS2__ */

#ifdef _WIN32
            if (errno == WSAEINPROGRESS)
#else /* ifndef _WIN32 */
            if (errno == EINPROGRESS)
#endif /* ndef _WIN32 || __OS2__ */
            {
                break;
            }

            if (errno != EINTR)
            {
                socket_error = errno;
                close_socket(fd);
                connect_failed = 1;
                break;
            }
        }
        if (connect_failed)
        {
            continue;
        }

#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
        if (flags != -1)
        {
            flags &= ~O_NDELAY;
            fcntl(fd, F_SETFL, flags);
        }
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */

        /* wait for connection to complete */
        FD_ZERO(&wfds);
        FD_SET(fd, &wfds);

        memset(&timeout, 0, sizeof(timeout));
        timeout.tv_sec  = 30;

        /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */
        if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0) && FD_ISSET(fd, &wfds))
        {
            socklen_t optlen = sizeof(socket_error);
            if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen))
            {
                if (!socket_error)
                {
                    /* Connection established, no need to try other addresses. */
                    break;
                }
                if (rp->ai_next != NULL)
                {
                    /*
                    * There's another address we can try, so log that this
                    * one didn't work out. If the last one fails, too,
                    * it will get logged outside the loop body so we don't
                    * have to mention it here.
                    */
                    log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.",
                      csp->http->host_ip_addr_str, service, strerror(socket_error));
                }
            }
            else
            {
                socket_error = errno;
                log_error(LOG_LEVEL_ERROR, "Could not get the state of "
                          "the connection to [%s]:%s: %s; dropping connection.",
                          csp->http->host_ip_addr_str, service, strerror(errno));
            }
        }

        /* Connection failed, try next address */
        close_socket(fd);
    }

    freeaddrinfo(result);
    if (!rp)
    {
        log_error(LOG_LEVEL_CONNECT, "Could not connect to [%s]:%s: %s.",
         host, service, strerror(socket_error));
        csp->error_message = strdup(strerror(socket_error));
        return(JB_INVALID_SOCKET);
    }
    log_error(LOG_LEVEL_CONNECT, "Connected to %s[%s]:%s.",
      host, csp->http->host_ip_addr_str, service);
    if (is_proxy) {
        log_time_stage(csp, TIME_STAGE_PROXY_CONNECTED);
    }else {
        log_time_stage(csp, TIME_STAGE_REMOTE_CONNECTED);
    }
    return(fd);

}