Exemplo n.º 1
0
/* cannot do this via _init, else static builds might spew error message
 * for every iptables invocation.
 */
static int connlabel_open(void)
{
	const char *fname;

	if (map)
		return 0;

	map = nfct_labelmap_new(NULL);
	if (map != NULL)
		return 0;

	fname = nfct_labels_get_path();
	if (errno) {
		fprintf(stderr, "Warning: cannot open %s: %s\n",
			fname, strerror(errno));
	} else {
		xtables_error(RESOURCE_PROBLEM,
			"cannot parse %s: no labels found", fname);
	}
	return 1;
}
Exemplo n.º 2
0
static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size,
				const char *events)
{
	char str[strlen(events) + 1], *e = str, *t;
	unsigned int mask = 0, i;

	strcpy(str, events);
	while ((t = strsep(&e, ","))) {
		for (i = 0; i < size; i++) {
			if (strcmp(t, tbl[i].name))
				continue;
			mask |= 1 << tbl[i].event;
			break;
		}

		if (i == size)
			xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t);
	}

	return mask;
}
Exemplo n.º 3
0
static int icmp_parse(int c, char **argv, int invert, unsigned int *flags,
                      const void *entry, struct xt_entry_match **match)
{
	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;

	switch (c) {
	case '1':
		if (*flags == 1)
			xtables_error(PARAMETER_PROBLEM,
				   "icmp match: only use --icmp-type once!");
		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
		parse_icmp(optarg, &icmpinfo->type, 
			   icmpinfo->code);
		if (invert)
			icmpinfo->invflags |= IPT_ICMP_INV;
		*flags = 1;
		break;
	}

	return 1;
}
Exemplo n.º 4
0
static void MYSNAT_parse(struct xt_option_call *cb)
{
	const struct ipt_entry *entry = cb->xt_entry;
	struct ipt_natinfo *info = (void *)(*cb->target);
	int portok;

	if (entry->ip.proto == IPPROTO_TCP
	    || entry->ip.proto == IPPROTO_UDP
	    || entry->ip.proto == IPPROTO_SCTP
	    || entry->ip.proto == IPPROTO_DCCP
	    || entry->ip.proto == IPPROTO_ICMP)
		portok = 1;
	else
		portok = 0;

	xtables_option_parse(cb);
	switch (cb->entry->id) {
	case O_TO_SRC:
		if (cb->xflags & F_X_TO_SRC) {
			if (!kernel_version)
				get_kernel_version();
			if (kernel_version > LINUX_VERSION(2, 6, 10))
				xtables_error(PARAMETER_PROBLEM,
					   "MYSNAT: Multiple --to-source not supported");
		}
		*cb->target = parse_to(cb->arg, portok, info);
		/* WTF do we need this for?? */
		if (cb->xflags & F_RANDOM)
			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
		cb->xflags |= F_X_TO_SRC;
		break;
	case O_RANDOM:
		if (cb->xflags & F_TO_SRC)
			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
		break;
	case O_PERSISTENT:
		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
		break;
	}
}
Exemplo n.º 5
0
static void ct_parse_zone_id(const char *opt, unsigned int opt_id,
			     uint16_t *zone_id, uint16_t *flags)
{
	if (opt_id == O_ZONE_ORIG)
		*flags |= XT_CT_ZONE_DIR_ORIG;
	if (opt_id == O_ZONE_REPLY)
		*flags |= XT_CT_ZONE_DIR_REPL;

	*zone_id = 0;

	if (strcasecmp(opt, "mark") == 0) {
		*flags |= XT_CT_ZONE_MARK;
	} else {
		uintmax_t val;

		if (!xtables_strtoul(opt, NULL, &val, 0, UINT16_MAX))
			xtables_error(PARAMETER_PROBLEM,
				      "Cannot parse %s as a zone ID\n", opt);

		*zone_id = (uint16_t)val;
	}
}
static void conntrack1_mt_parse(struct xt_option_call *cb)
{
	struct xt_conntrack_mtinfo1 *info = cb->data;
	struct xt_conntrack_mtinfo3 up;

	memset(&up, 0, sizeof(up));
	cinfo_transform(&up, info);
	up.origsrc_port_high = up.origsrc_port;
	up.origdst_port_high = up.origdst_port;
	up.replsrc_port_high = up.replsrc_port;
	up.repldst_port_high = up.repldst_port;
	cb->data = &up;
	conntrack_mt_parse(cb, 3);
	if (up.origsrc_port != up.origsrc_port_high ||
	    up.origdst_port != up.origdst_port_high ||
	    up.replsrc_port != up.replsrc_port_high ||
	    up.repldst_port != up.repldst_port_high)
		xtables_error(PARAMETER_PROBLEM,
			"conntrack rev 1 does not support port ranges");
	cinfo_transform(info, &up);
	cb->data = info;
}
Exemplo n.º 7
0
static void brmark_print(const void *ip, const struct xt_entry_target *target,
			 int numeric)
{
	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)target->data;
	int tmp;

	tmp = info->target & ~EBT_VERDICT_BITS;
	if (tmp == MARK_SET_VALUE)
		printf("--mark-set");
	else if (tmp == MARK_OR_VALUE)
		printf("--mark-or");
	else if (tmp == MARK_XOR_VALUE)
		printf("--mark-xor");
	else if (tmp == MARK_AND_VALUE)
		printf("--mark-and");
	else
		xtables_error(PARAMETER_PROBLEM, "Unknown mark action");

	printf(" 0x%lx", info->mark);
	tmp = info->target | ~EBT_VERDICT_BITS;
	printf(" --mark-target %s", ebt_target_name(tmp));
}
static void
parse_udp_ports(const char *portstring, u_int16_t *ports)
{
	char *buffer;
	char *cp;

	buffer = strdup(portstring);
	if ((cp = strchr(buffer, ':')) == NULL)
		ports[0] = ports[1] = xtables_parse_port(buffer, "udp");
	else {
		*cp = '\0';
		cp++;

		ports[0] = buffer[0] ? xtables_parse_port(buffer, "udp") : 0;
		ports[1] = cp[0] ? xtables_parse_port(cp, "udp") : 0xFFFF;

		if (ports[0] > ports[1])
			xtables_error(PARAMETER_PROBLEM,
				   "invalid portrange (min > max)");
	}
	free(buffer);
}
Exemplo n.º 9
0
/* Debugging prototype. */
static int for_each_table(int (*func)(const char *tablename))
{
	int ret = 1;
	FILE *procfile = NULL;
	char tablename[XT_TABLE_MAXNAMELEN+1];

	procfile = fopen("/proc/net/ip6_tables_names", "re");
	if (!procfile)
		return ret;

	while (fgets(tablename, sizeof(tablename), procfile)) {
		if (tablename[strlen(tablename) - 1] != '\n')
			xtables_error(OTHER_PROBLEM,
				   "Badly formed tablename `%s'\n",
				   tablename);
		tablename[strlen(tablename) - 1] = '\0';
		ret &= func(tablename);
	}

	fclose(procfile);
	return ret;
}
Exemplo n.º 10
0
static int
comment_parse(int c, char **argv, int invert, unsigned int *flags,
              const void *entry, struct xt_entry_match **match)
{
	struct xt_comment_info *commentinfo = (struct xt_comment_info *)(*match)->data;

	switch (c) {
	case '1':
		xtables_check_inverse(argv[optind-1], &invert, &optind, 0);
		if (invert) {
			xtables_error(PARAMETER_PROBLEM,
					"Sorry, you can't have an inverted comment");
		}
		parse_comment(argv[optind-1], commentinfo);
		*flags = 1;
		break;

	default:
		return 0;
	}
	return 1;
}
Exemplo n.º 11
0
static void iprange_parse_range(const char *oarg, union nf_inet_addr *range,
				uint8_t family, const char *optname)
{
	char *arg = strdup(oarg);
	char *dash;

	if (arg == NULL)
		xtables_error(RESOURCE_PROBLEM, "strdup");
	dash = strchr(arg, '-');
	if (dash == NULL) {
		iprange_parse_spec(arg, arg, range, family, optname);
		free(arg);
		return;
	}

	*dash = '\0';
	iprange_parse_spec(arg, dash + 1, range, family, optname);
	if (memcmp(&range[0], &range[1], sizeof(*range)) > 0)
		fprintf(stderr, "xt_iprange: range %s-%s is reversed and "
			"will never match\n", arg, dash + 1);
	free(arg);
}
Exemplo n.º 12
0
static int ah_parse(int c, char **argv, int invert, unsigned int *flags,
                    const void *entry, struct xt_entry_match **match)
{
	struct ipt_ah *ahinfo = (struct ipt_ah *)(*match)->data;

	switch (c) {
	case '1':
		if (*flags & AH_SPI)
			xtables_error(PARAMETER_PROBLEM,
				   "Only one `--ahspi' allowed");
		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
		parse_ah_spis(optarg, ahinfo->spis);
		if (invert)
			ahinfo->invflags |= IPT_AH_INV_SPI;
		*flags |= AH_SPI;
		break;
	default:
		return 0;
	}

	return 1;
}
Exemplo n.º 13
0
static void REJECT_parse(struct xt_option_call *cb)
{
	struct ipt_reject_info *reject = cb->data;
	unsigned int i;

	xtables_option_parse(cb);
	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
		if (strncasecmp(reject_table[i].name,
		      cb->arg, strlen(cb->arg)) == 0 ||
		    strncasecmp(reject_table[i].alias,
		      cb->arg, strlen(cb->arg)) == 0) {
			reject->with = reject_table[i].with;
			return;
		}
	/* This due to be dropped late in 2.4 pre-release cycle --RR */
	if (strncasecmp("echo-reply", cb->arg, strlen(cb->arg)) == 0 ||
	    strncasecmp("echoreply", cb->arg, strlen(cb->arg)) == 0)
		fprintf(stderr, "--reject-with echo-reply no longer"
			" supported\n");
	xtables_error(PARAMETER_PROBLEM,
		"unknown reject type \"%s\"", cb->arg);
}
static void ULOG_parse(struct xt_option_call *cb)
{
	struct ipt_ulog_info *loginfo = cb->data;

	xtables_option_parse(cb);
	switch (cb->entry->id) {
	case O_ULOG_NLGROUP:
		loginfo->nl_group = 1 << (cb->val.u8 - 1);
		break;
	case O_ULOG_PREFIX:
		if (strchr(cb->arg, '\n') != NULL)
			xtables_error(PARAMETER_PROBLEM,
				   "Newlines not allowed in --ulog-prefix");
		break;
	case O_ULOG_CPRANGE:
		loginfo->copy_range = cb->val.u64;
		break;
	case O_ULOG_QTHR:
		loginfo->qthreshold = cb->val.u64;
		break;
	}
}
Exemplo n.º 15
0
/* Function which parses command options; returns true if it
   ate an option */
static int
daddr_tg_parse(int c, char **argv, int invert, unsigned int *flags,
	       const void *entry, struct xt_entry_target **target,
	       parse_daddr_fn da_fn)
{
	struct xt_daddr_tginfo *daddrinfo
		= (struct xt_daddr_tginfo *)(*target)->data;

	switch (c) {
	case '1':
		if (*flags)
			xtables_error(PARAMETER_PROBLEM,
			           "DADDR target: Cant specify --set-daddr twice");
		(da_fn)(optarg, daddrinfo);
		*flags = 1;
		break;

	default:
		return 0;
	}

	return 1;
}
Exemplo n.º 16
0
static int
dns_parse(int c, char **argv, int invert, unsigned int *flags,
          const void *entry, struct xt_entry_match **match)
{
	struct xt_dns_info *dnsinfo = (struct xt_dns_info *)(*match)->data;

	switch (c) {
	case '1':
		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
		if (str2qn(optarg, dnsinfo->fqdn, sizeof(dnsinfo->fqdn)))
			xtables_error(PARAMETER_PROBLEM, "--dns-fqdn invalid "
				      "fqdn");
		if (invert)
			dnsinfo->invert = 1;
		*flags = 1;
		break;

	default:
		return 0;
	}

	return 1;
}
static int mh_parse(int c, char **argv, int invert, unsigned int *flags,
                    const void *entry, struct xt_entry_match **match)
{
	struct ip6t_mh *mhinfo = (struct ip6t_mh *)(*match)->data;

	switch (c) {
	case '1':
		if (*flags & MH_TYPES)
			xtables_error(PARAMETER_PROBLEM,
				   "Only one `--mh-type' allowed");
		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
		parse_mh_types(optarg, mhinfo->types);
		if (invert)
			mhinfo->invflags |= IP6T_MH_INV_TYPE;
		*flags |= MH_TYPES;
		break;

	default:
		return 0;
	}

	return 1;
}
Exemplo n.º 18
0
static void connlabel_mt_parse(struct xt_option_call *cb)
{
	struct xt_connlabel_mtinfo *info = cb->data;
	int tmp;

	connlabel_open();
	xtables_option_parse(cb);

	switch (cb->entry->id) {
	case O_LABEL:
		tmp = nfct_labelmap_get_bit(map, cb->arg);
		if (tmp < 0)
			xtables_error(PARAMETER_PROBLEM, "label '%s' not found", cb->arg);
		info->bit = tmp;
		if (cb->invert)
			info->options |= XT_CONNLABEL_OP_INVERT;
		break;
	case O_SET:
		info->options |= XT_CONNLABEL_OP_SET;
		break;
	}

}
Exemplo n.º 19
0
static uint16_t
parse_dccp_types(const char *typestring)
{
	uint16_t typemask = 0;
	char *ptr, *buffer;

	buffer = strdup(typestring);

	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
		unsigned int i;
		for (i = 0; i < ARRAY_SIZE(dccp_pkt_types); ++i)
			if (!strcasecmp(dccp_pkt_types[i], ptr)) {
				typemask |= (1 << i);
				break;
			}
		if (i == ARRAY_SIZE(dccp_pkt_types))
			xtables_error(PARAMETER_PROBLEM,
				   "Unknown DCCP type `%s'", ptr);
	}

	free(buffer);
	return typemask;
}
Exemplo n.º 20
0
static void
conntrack_ps_expires(struct xt_conntrack_mtinfo2 *info, const char *s)
{
	unsigned int min, max;
	char *end;

	if (!xtables_strtoui(s, &end, &min, 0, UINT32_MAX))
		xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
	max = min;
	if (*end == ':')
		if (!xtables_strtoui(s, &end, &max, 0, UINT32_MAX))
			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
	if (*end != '\0')
		xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);

	if (min > max)
		xtables_error(PARAMETER_PROBLEM,
		           "expire min. range value \"%u\" greater than max. "
		           "range value \"%u\"", min, max);

	info->expires_min = min;
	info->expires_max = max;
}
Exemplo n.º 21
0
static int tos_tg_parse_v0(int c, char **argv, int invert, unsigned int *flags,
                           const void *entry, struct xt_entry_target **target)
{
	struct ipt_tos_target_info *info = (void *)(*target)->data;
	struct tos_value_mask tvm;

	switch (c) {
	case '=':
		xtables_param_act(XTF_ONLY_ONCE, "TOS", "--set-tos", *flags & FLAG_TOS);
		xtables_param_act(XTF_NO_INVERT, "TOS", "--set-tos", invert);
		if (!tos_parse_symbolic(optarg, &tvm, 0xFF))
			xtables_param_act(XTF_BAD_VALUE, "TOS", "--set-tos", optarg);
		if (tvm.mask != 0xFF)
			xtables_error(PARAMETER_PROBLEM, "tos match: Your kernel "
			           "is too old to support anything besides "
				   "/0xFF as a mask.");
		info->tos = tvm.value;
		*flags |= FLAG_TOS;
		return true;
	}

	return false;
}
Exemplo n.º 22
0
static void
parse_tos(const char *s, struct ipt_tos_target_info *info)
{
	unsigned int i, tos;

	if (xtables_strtoui(s, NULL, &tos, 0, UINT8_MAX)) {
		if (tos == IPTOS_LOWDELAY
		    || tos == IPTOS_THROUGHPUT
		    || tos == IPTOS_RELIABILITY
		    || tos == IPTOS_MINCOST
		    || tos == IPTOS_NORMALSVC) {
		    	info->tos = (u_int8_t )tos;
		    	return;
		}
	} else {
		for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
			if (strcasecmp(s,TOS_values[i].name) == 0) {
				info->tos = TOS_values[i].TOS;
				return;
			}
	}
	xtables_error(PARAMETER_PROBLEM, "Bad TOS value `%s'", s);
}
Exemplo n.º 23
0
/* Function which parses command options; returns true if it
   ate an option */
static int
parse(int c, char **argv, int invert, unsigned int *flags,
      const void *entry,
      struct xt_entry_target **target)
{
	struct ipt_tos_target_info *tosinfo
		= (struct ipt_tos_target_info *)(*target)->data;

	switch (c) {
	case '1':
		if (*flags)
			xtables_error(PARAMETER_PROBLEM,
			           "TOS target: Cant specify --set-tos twice");
		parse_tos(optarg, tosinfo);
		*flags = 1;
		break;

	default:
		return 0;
	}

	return 1;
}
Exemplo n.º 24
0
static int ct_parse(int c, char **argv, int invert, unsigned int *flags,
		    const void *entry, struct xt_entry_target **target)
{
	struct xt_ct_target_info *info = (struct xt_ct_target_info *)(*target)->data;
	unsigned int zone;

	switch (c) {
	case CT_OPT_NOTRACK:
		xtables_param_act(XTF_ONLY_ONCE, "CT", "--notrack", *flags & CT_OPT_NOTRACK);
		info->flags |= XT_CT_NOTRACK;
		break;
	case CT_OPT_HELPER:
		xtables_param_act(XTF_ONLY_ONCE, "CT", "--helper", *flags & CT_OPT_HELPER);
		strncpy(info->helper, optarg, sizeof(info->helper));
		info->helper[sizeof(info->helper) - 1] = '\0';
		break;
	case CT_OPT_CTEVENTS:
		xtables_param_act(XTF_ONLY_ONCE, "CT", "--ctevents", *flags & CT_OPT_CTEVENTS);
		info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), optarg);
		break;
	case CT_OPT_EXPEVENTS:
		xtables_param_act(XTF_ONLY_ONCE, "CT", "--expevents", *flags & CT_OPT_EXPEVENTS);
		info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), optarg);
		break;
	case CT_OPT_ZONE:
		xtables_param_act(XTF_ONLY_ONCE, "CT", "--zone", *flags & CT_OPT_ZONE);
		if (!xtables_strtoui(optarg, NULL, &zone, 0, UINT16_MAX))
			xtables_error(PARAMETER_PROBLEM, "Bad zone value \"%s\"", optarg);
		info->zone = zone;
		break;
	default:
		return 0;
	}

	*flags |= c;
	return 1;
}
Exemplo n.º 25
0
static void
parse_interface(const char *arg, char *vianame, unsigned char *mask)
{
	int vialen = strlen(arg);
	unsigned int i;

	memset(mask, 0, IFNAMSIZ);
	memset(vianame, 0, IFNAMSIZ);

	if (vialen + 1 > IFNAMSIZ)
		xtables_error(PARAMETER_PROBLEM,
			      "interface name `%s' must be shorter than IFNAMSIZ"
			      " (%i)", arg, IFNAMSIZ-1);

	strcpy(vianame, arg);
	if (vialen == 0)
		memset(mask, 0, IFNAMSIZ);
	else if (vianame[vialen - 1] == '+') {
		memset(mask, 0xFF, vialen - 1);
		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
		/* Don't remove `+' here! -HW */
	} else {
		/* Include nul-terminator in match */
		memset(mask, 0xFF, vialen + 1);
		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
		for (i = 0; vianame[i]; i++) {
			if (!isalnum(vianame[i])
			    && vianame[i] != '_'
			    && vianame[i] != '.') {
				printf("Warning: wierd character in interface"
				       " `%s' (No aliases, :, ! or *).\n",
				       vianame);
				break;
			}
		}
	}
}
Exemplo n.º 26
0
static int
length_parse(int c, char **argv, int invert, unsigned int *flags,
             const void *entry, struct xt_entry_match **match)
{
	struct xt_length_info *info = (struct xt_length_info *)(*match)->data;

	switch (c) {
		case '1':
			if (*flags)
				xtables_error(PARAMETER_PROBLEM,
				           "length: `--length' may only be "
				           "specified once");
			xtables_check_inverse(optarg, &invert, &optind, 0);
			parse_lengths(argv[optind-1], info);
			if (invert)
				info->invert = 1;
			*flags = 1;
			break;
			
		default:
			return 0;
	}
	return 1;
}
static int
tcpmss_parse(int c, char **argv, int invert, unsigned int *flags,
             const void *entry, struct xt_entry_match **match)
{
	struct xt_tcpmss_match_info *mssinfo =
		(struct xt_tcpmss_match_info *)(*match)->data;

	switch (c) {
	case '1':
		if (*flags)
			xtables_error(PARAMETER_PROBLEM,
				   "Only one `--mss' allowed");
		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
		parse_tcp_mssvalues(optarg,
				    &mssinfo->mss_min, &mssinfo->mss_max);
		if (invert)
			mssinfo->invert = 1;
		*flags = 1;
		break;
	default:
		return 0;
	}
	return 1;
}
Exemplo n.º 28
0
/* If a single value is provided, min and max are both set to the value */
static void
parse_lengths(const char *s, struct xt_length_info *info)
{
	char *buffer;
	char *cp;

	buffer = strdup(s);
	if ((cp = strchr(buffer, ':')) == NULL)
		info->min = info->max = parse_length(buffer);
	else {
		*cp = '\0';
		cp++;

		info->min = buffer[0] ? parse_length(buffer) : 0;
		info->max = cp[0] ? parse_length(cp) : 0xFFFF;
	}
	free(buffer);
	
	if (info->min > info->max)
		xtables_error(PARAMETER_PROBLEM,
		           "length min. range value `%u' greater than max. "
		           "range value `%u'", info->min, info->max);
	
}
Exemplo n.º 29
0
static int
helper_parse(int c, char **argv, int invert, unsigned int *flags,
             const void *entry, struct xt_entry_match **match)
{
	struct xt_helper_info *info = (struct xt_helper_info *)(*match)->data;

	switch (c) {
	case '1':
		if (*flags)
			xtables_error(PARAMETER_PROBLEM,
					"helper match: Only use --helper ONCE!");
		xtables_check_inverse(optarg, &invert, &invert, 0);
		strncpy(info->name, optarg, 29);
		info->name[29] = '\0';
		if (invert)
			info->invert = 1;
		*flags = 1;
		break;

	default:
		return 0;
	}
	return 1;
}
Exemplo n.º 30
0
static unsigned int
parse_tcp_flag(const char *flags)
{
	unsigned int ret = 0;
	char *ptr;
	char *buffer;

	buffer = strdup(flags);

	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
		unsigned int i;
		for (i = 0; i < ARRAY_SIZE(tcp_flag_names); ++i)
			if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
				ret |= tcp_flag_names[i].flag;
				break;
			}
		if (i == ARRAY_SIZE(tcp_flag_names))
			xtables_error(PARAMETER_PROBLEM,
				   "Unknown TCP flag `%s'", ptr);
	}

	free(buffer);
	return ret;
}