Пример #1
0
static int
table_mysql_check(int service, struct dict *params, const char *key)
{
	MYSQL_STMT	*stmt;
	int		 r, s;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

	stmt = table_mysql_query(key, service);
	if (stmt == NULL)
		return (-1);

	r = -1;
	s = mysql_stmt_fetch(stmt);

	if (s == 0)
		r = 1;
	else if (s == MYSQL_NO_DATA)
		r = 0;
	else
		log_warnx("warn: table-mysql: mysql_stmt_fetch: %s",
		    mysql_stmt_error(stmt));

	if (mysql_stmt_free_result(stmt))
		log_warnx("warn: table-mysql: mysql_stmt_free_result: %s",
		    mysql_stmt_error(stmt));

	return (r);
}
Пример #2
0
static int
table_redis_check(int service, const char *key)
{
	int		 r;
	redisReply	*reply;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

	reply = table_redis_query(key, service);
	if (reply == NULL)
		return (-1);

	switch (reply->type) {
		case REDIS_REPLY_INTEGER:
		case REDIS_REPLY_STRING:
		case REDIS_REPLY_ARRAY:
			r = 1;
			break;
		
		case REDIS_REPLY_NIL:
			r = 0;
			break;

		case REDIS_REPLY_STATUS:
		case REDIS_REPLY_ERROR:
		default:
			r = -1;
			break;
	}

	freeReplyObject(reply);

	return (r);
}
Пример #3
0
static MYSQL_STMT *
table_mysql_query(const char *key, int service)
{
	MYSQL_STMT	*stmt;
	MYSQL_BIND	 param[1];
	unsigned long	 keylen;
	char		 buffer[SMTPD_MAXLINESIZE];
	int		 i;

    retry:

	stmt = NULL;
	for(i = 0; i < SQL_MAX; i++)
		if (service == 1 << i) {
			stmt = config->statements[i];
			break;
		}

	if (stmt == NULL)
		return (NULL);

	if (strlcpy(buffer, key, sizeof(buffer)) >= sizeof(buffer)) {
		log_warnx("warn: table-mysql: key too long: \"%s\"", key);
		return (NULL);
	}

	keylen = strlen(key);

	param[0].buffer_type = MYSQL_TYPE_STRING;
	param[0].buffer = buffer;
	param[0].buffer_length = sizeof(buffer);
	param[0].is_null = 0;
	param[0].length = &keylen;

	if (mysql_stmt_bind_param(stmt, param)) {
		log_warnx("warn: table-mysql: mysql_stmt_bind_param: %s",
		    mysql_stmt_error(stmt));
		return (NULL);
	}

	if (mysql_stmt_execute(stmt)) {
		if (mysql_stmt_errno(stmt) == CR_SERVER_LOST ||
		    mysql_stmt_errno(stmt) == CR_SERVER_GONE_ERROR ||
		    mysql_stmt_errno(stmt) == CR_COMMANDS_OUT_OF_SYNC) {
			log_warnx("warn: table-mysql: trying to reconnect after error: %s",
			    mysql_stmt_error(stmt));
			if (config_connect(config))
				goto retry;
			return (NULL);
		}
		log_warnx("warn: table-mysql: mysql_stmt_execute: %s",
		    mysql_stmt_error(stmt));
		return (NULL);
	}

	return (stmt);
}
Пример #4
0
int
main(int argc, char **argv)
{
	int	ch, i;

	log_init(1);
	log_verbose(~0);

	while ((ch = getopt(argc, argv, "")) != -1) {
		switch (ch) {
		default:
			log_warnx("warn: table-mysql: bad option");
			return (1);
			/* NOTREACHED */
		}
	}
	argc -= optind;
	argv += optind;

	if (argc != 1) {
		log_warnx("warn: table-mysql: bogus argument(s)");
		return (1);
	}

	conffile = argv[0];

	for (i = 0; i < SQL_MAX_RESULT; i++) {
		results[i].buffer_type = MYSQL_TYPE_STRING;
		results[i].buffer = results_buffer[i];
		results[i].buffer_length = SMTPD_MAXLINESIZE;
		results[i].is_null = 0;
	}

	config = config_load(conffile);
	if (config == NULL) {
		log_warnx("warn: table-mysql: error parsing config file");
		return (1);
	}
	if (config_connect(config) == 0) {
		log_warnx("warn: table-mysql: could not connect");
		return (1);
	}

	table_api_on_update(table_mysql_update);
	table_api_on_check(table_mysql_check);
	table_api_on_lookup(table_mysql_lookup);
	table_api_on_fetch(table_mysql_fetch);
	table_api_dispatch();

	return (0);
}
Пример #5
0
static int
table_redis_update(void)
{
	struct config	*c;

	if ((c = config_load(conffile)) == NULL)
		return (0);
	if (config_connect(c) == 0) {
		config_free(c);
		return (0);
	}

	config_free(config);
	config = c;
	return (1);
}
Пример #6
0
static int
table_postgres_update(void)
{
	struct config	*c;

	if ((c = config_load(conffile)) == NULL)
		return 0;
	if (config_connect(c) == 0) {
		config_free(c);
		return 0;
	}

	config_free(config);
	config = c;

	return 1;
}
Пример #7
0
int
main(int argc, char **argv)
{
	int	ch;

	log_init(1);
	log_verbose(~0);

	while ((ch = getopt(argc, argv, "")) != -1) {
		switch (ch) {
		default:
			log_warnx("warn: table-postgres: bad option");
			return (1);
			/* NOTREACHED */
		}
	}
	argc -= optind;
	argv += optind;

	if (argc != 1) {
		log_warnx("warn: table-postgres: bogus argument(s)");
		return (1);
	}

	conffile = argv[0];

	config = config_load(conffile);
	if (config == NULL) {
		log_warnx("warn: table-postgres: error parsing config file");
		return (1);
	}
	if (config_connect(config) == 0) {
		log_warnx("warn: table-postgres: could not connect");
		return (1);
	}

	table_api_on_update(table_postgres_update);
	table_api_on_check(table_postgres_check);
	table_api_on_lookup(table_postgres_lookup);
	table_api_on_fetch(table_postgres_fetch);
	table_api_dispatch();

	return (0);
}
Пример #8
0
static int
table_postgres_check(int service, const char *key)
{
	PGresult	*res;
	int		 r;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

	res = table_postgres_query(key, service);
	if (res == NULL)
		return (-1);

	r = (PQntuples(res) == 0) ? 0 : 1;

	PQclear(res);

	return (r);
}
Пример #9
0
static PGresult *
table_postgres_query(const char *key, int service)
{
	PGresult	*res;
	const char	*errfld;
	char		*stmt;
	int		 i;

    retry:

	stmt = NULL;
	for(i = 0; i < SQL_MAX; i++)
		if (service == 1 << i) {
			stmt = config->statements[i];
			break;
		}

	if (stmt == NULL)
		return NULL;

	res = PQexecPrepared(config->db, stmt, 1, &key, NULL, NULL, 0);

	if (PQresultStatus(res) != PGRES_TUPLES_OK) {
		errfld = PQresultErrorField(res, PG_DIAG_SQLSTATE);
		/* PQresultErrorField can return NULL if the connection to the server
		   suddenly closed (e.g. server restart) */
		if (errfld == NULL || (errfld[0] == '0' && errfld[1] == '8')) {
			log_warnx("warn: table-postgres: trying to reconnect after error: %s",
			    PQerrorMessage(config->db));
			PQclear(res);
			if (config_connect(config))
				goto retry;
			return NULL;
		}
		log_warnx("warn: table-postgres: PQexecPrepared: %s",
		    PQerrorMessage(config->db));
		PQclear(res);
		return NULL;
	}

	return res;
}
Пример #10
0
static redisReply *
table_redis_query(const char *key, int service)
{
	redisReply	*res;
	char		*stmt;
	int		i;
	int		retry_times;

	retry_times = 3;

retry:
	--retry_times;
	if (retry_times < 0) {
		log_warnx("warn: table-redis: giving up: too many retries");
		return (NULL);
	} 

	stmt = NULL;
	for(i = 0; i < SQL_MAX; i++)
		if (service == 1 << i) {
			stmt = config->statements[i];
			break;
		}

	if (stmt == NULL)
		return (NULL);

	res = redisCommand(config->db, stmt, key);
	if (res == NULL) {
		log_warnx("warn: table-redis: redisCommand: %s",
		    config->db->errstr);

		if (config_connect(config))
			goto retry;

		return (NULL);
	}

	return (res);
}
Пример #11
0
static int
table_postgres_fetch(int service, char *dst, size_t sz)
{
	char		*stmt;
	PGresult	*res;
	const char	*k, *errfld;
	int		 i;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

    retry:

	if (service != K_SOURCE)
		return (-1);

	stmt = config->stmt_fetch_source;

	if (stmt == NULL)
		return (-1);

	if (config->source_ncall < config->source_refresh &&
	    time(NULL) - config->source_update < config->source_expire)
	    goto fetch;

	res = PQexecPrepared(config->db, stmt, 0, NULL, NULL, NULL, 0);

	if (PQresultStatus(res) != PGRES_TUPLES_OK) {
		errfld = PQresultErrorField(res, PG_DIAG_SQLSTATE);
		if (errfld[0] == '0' && errfld[1] == '8') {
			log_warnx("warn: table-postgres: trying to reconnect after error: %s",
			    PQerrorMessage(config->db));
			PQclear(res);
			if (config_connect(config))
				goto retry;
			return (-1);
		}
		log_warnx("warn: table-postgres: PQexecPrepared: %s",
		    PQerrorMessage(config->db));
		PQclear(res);
		return (-1);
	}

	config->source_iter = NULL;
	while (dict_poproot(&config->sources, NULL))
		;

	for (i = 0; i < PQntuples(res); i++)
		dict_set(&config->sources, PQgetvalue(res, i, 0), NULL);

	PQclear(res);

	config->source_update = time(NULL);
	config->source_ncall = 0;

    fetch:

	config->source_ncall += 1;

	if (! dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL)) {
		config->source_iter = NULL;
		if (! dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL))
			return (0);
	}

	if (strlcpy(dst, k, sz) >= sz)
		return (-1);

	return (1);
}
Пример #12
0
static int
table_postgres_lookup(int service, const char *key, char *dst, size_t sz)
{
	PGresult	*res;
	int		 r, i;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

	res = table_postgres_query(key, service);
	if (res == NULL)
		return (-1);

	if (PQntuples(res) == 0) {
		r = 0;
		goto end;
	}

	r = 1;
	switch(service) {
	case K_ALIAS:
		memset(dst, 0, sz);
		for (i = 0; i < PQntuples(res); i++) {
			if (dst[0] && strlcat(dst, ", ", sz) >= sz) {
				log_warnx("warn: table-postgres: result too large");
				r = -1;
				break;
			}
			if (strlcat(dst, PQgetvalue(res, i, 0), sz) >= sz) {
				log_warnx("warn: table-postgres: result too large");
				r = -1;
				break;
			}
		}
		break;
	case K_CREDENTIALS:
		if (snprintf(dst, sz, "%s:%s",
		    PQgetvalue(res, 0, 0),
 		    PQgetvalue(res, 0, 1)) > (ssize_t)sz) {
			log_warnx("warn: table-postgres: result too large");
			r = -1;
		}
		break;
	case K_USERINFO:
		if (snprintf(dst, sz, "%s:%s:%s",
		    PQgetvalue(res, 0, 0),
		    PQgetvalue(res, 0, 1),
		    PQgetvalue(res, 0, 2)) > (ssize_t)sz) {
			log_warnx("warn: table-postgres: result too large");
			r = -1;
		}
		break;
	case K_DOMAIN:
	case K_NETADDR:
	case K_SOURCE:
	case K_MAILADDR:
	case K_ADDRNAME:
		if (strlcpy(dst, PQgetvalue(res, 0, 0), sz) >= sz) {
			log_warnx("warn: table-postgres: result too large");
			r = -1;
		}
		break;
	default:
		log_warnx("warn: table-postgres: unknown service %d",
		    service);
		r = -1;
	}

    end:
	PQclear(res);

	return (r);
}
Пример #13
0
static int
table_mysql_fetch(int service, char *dst, size_t sz)
{
	MYSQL_STMT	*stmt;
	const char	*k;
	int		 s;

    retry:

	if (service != K_SOURCE)
		return (-1);

	stmt = config->stmt_fetch_source;

	if (stmt == NULL)
		return (-1);

	if (config->source_ncall < config->source_refresh &&
	    time(NULL) - config->source_update < config->source_expire)
	    goto fetch;

	if (mysql_stmt_execute(stmt)) {
		if (mysql_stmt_errno(stmt) == CR_SERVER_LOST ||
		    mysql_stmt_errno(stmt) == CR_SERVER_GONE_ERROR ||
		    mysql_stmt_errno(stmt) == CR_COMMANDS_OUT_OF_SYNC) {
			log_warnx("warn: table-mysql: trying to reconnect after error: %s",
			    mysql_stmt_error(stmt));
			if (config_connect(config))
				goto retry;
			return (-1);
		}
		log_warnx("warn: table-mysql: mysql_stmt_execute: %s",
		    mysql_stmt_error(stmt));
		return (-1);
	}

	config->source_iter = NULL;
	while(dict_poproot(&config->sources, NULL, NULL))
		;

	while ((s = mysql_stmt_fetch(stmt)) == 0)
		dict_set(&config->sources, results_buffer[0], NULL);

	if (s && s != MYSQL_NO_DATA)
		log_warnx("warn: table-mysql: mysql_stmt_fetch: %s",
		    mysql_stmt_error(stmt));

	if (mysql_stmt_free_result(stmt))
		log_warnx("warn: table-mysql: mysql_stmt_free_result: %s",
		    mysql_stmt_error(stmt));

	config->source_update = time(NULL);
	config->source_ncall = 0;

    fetch:

	config->source_ncall += 1;

	if (! dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL)) {
		config->source_iter = NULL;
		if (! dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL))
			return (0);
	}

	if (strlcpy(dst, k, sz) >= sz)
		return (-1);

	return (1);
}
Пример #14
0
static int
table_mysql_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz)
{
	MYSQL_STMT	*stmt;
	int		 r, s;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

	stmt = table_mysql_query(key, service);
	if (stmt == NULL)
		return (-1);

	s = mysql_stmt_fetch(stmt);
	if (s == MYSQL_NO_DATA) {
		r = 0;
		goto end;
	}
	
	if (s != 0) {
		r = -1;
		log_warnx("warn: table-mysql: mysql_stmt_fetch: %s",
		    mysql_stmt_error(stmt));
		goto end;
	}

	r = 1;

	switch(service) {
	case K_ALIAS:
		memset(dst, 0, sz);
		do {
			if (dst[0] && strlcat(dst, ", ", sz) >= sz) {
				log_warnx("warn: table-mysql: result too large");
				r = -1;
				break;
			}
			if (strlcat(dst, results_buffer[0], sz) >= sz) {
				log_warnx("warn: table-mysql: result too large");
				r = -1;
				break;
			}
			s = mysql_stmt_fetch(stmt);
		} while (s == 0);

		if (s && s != MYSQL_NO_DATA) {
			log_warnx("warn: table-mysql: mysql_stmt_fetch: %s",
			    mysql_stmt_error(stmt));
			r = -1;
		}
		break;
	case K_CREDENTIALS:
		if (snprintf(dst, sz, "%s:%s",
		    results_buffer[0],
		    results_buffer[1]) > (ssize_t)sz) {
			log_warnx("warn: table-mysql: result too large");
			r = -1;
		}
		break;
	case K_USERINFO:
		if (snprintf(dst, sz, "%s:%s:%s",
		    results_buffer[0],
		    results_buffer[1],
		    results_buffer[2]) > (ssize_t)sz) {
			log_warnx("warn: table-mysql: result too large");
			r = -1;
		}
		break;
	case K_DOMAIN:
	case K_NETADDR:
	case K_SOURCE:
	case K_MAILADDR:
	case K_ADDRNAME:
		if (strlcpy(dst, results_buffer[0], sz) >= sz) {
			log_warnx("warn: table-mysql: result too large");
			r = -1;
		}
		break;
	default:
		log_warnx("warn: table-mysql: unknown service %d",
		    service);
		r = -1;
	}

    end:
	if (mysql_stmt_free_result(stmt))
		log_warnx("warn: table-mysql: mysql_stmt_free_result: %s",
		    mysql_stmt_error(stmt));

	return (r);
}
Пример #15
0
static int
table_redis_lookup(int service, const char *key, char *dst, size_t sz)
{
	redisReply	*reply, *elmt;
	unsigned int	i;
	int		r;

	if (config->db == NULL && config_connect(config) == 0)
		return (-1);

	reply = table_redis_query(key, service);
	if (reply == NULL)
		return (-1);

	r = 1;
	switch(service) {
	case K_ALIAS:
		if (reply->type == REDIS_REPLY_STRING) {
			if (dst[0] && strlcat(dst, ", ", sz) >= sz) {
				log_warnx("warn: table-redis: result too large");
				r = -1;
			}
			if (strlcat(dst, reply->str, sz) >= sz) {
				log_warnx("warn: table-redis: result too large");
				r = -1;
			}
		}
		else if (reply->type == REDIS_REPLY_ARRAY) {
			if (reply->elements == 0)
				r = 0;
			
			for (i = 0; i < reply->elements; i++) {
				elmt = reply->element[i];
				if (elmt == NULL ||
				    elmt->type != REDIS_REPLY_STRING) {
					r = -1;
					break;
				}
				if (dst[0] && strlcat(dst, ", ", sz) >= sz) {
					log_warnx("warn: table-redis: result too large");
					r = -1;
				}
				if (strlcat(dst, elmt->str, sz) >= sz) {
					log_warnx("warn: table-redis: result too large");
					r = -1;
				}
			}
		}
		else
			r = -1;
		break;
	case K_CREDENTIALS:
	case K_USERINFO:
	case K_DOMAIN:
	case K_NETADDR:
	case K_SOURCE:
	case K_MAILADDR:
	case K_ADDRNAME:
		if (reply->type == REDIS_REPLY_STRING) {
			if (strlcpy(dst, reply->str, sz) >= sz) {
				log_warnx("warn: table-redis: result too large");
				r = -1;
			}
		}
		else
			r = -1;
		break;
	default:
		log_warnx("warn: table-redis: unknown service %d",
		    service);
		r = -1;
	}

end:
	freeReplyObject(reply);
	return (r);
}