authd: add improved API for internal usage

This is similar to what exists in ircd, but instead of request ID's, we
return struct dns_query pointers (that are freed by the DNS callback, so
you don't have to worry about their lifecycle management).
This commit is contained in:
Elizabeth Myers 2016-03-12 07:08:27 -06:00
parent 27aca3c385
commit 399c633313
2 changed files with 214 additions and 87 deletions

View file

@ -1,4 +1,4 @@
/* authd/dns.h - header for authd DNS functions
/* authd/dns.c - authd DNS functions
* Copyright (c) 2016 William Pitcock <nenolod@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
@ -22,120 +22,228 @@
#include "dns.h"
#include "res.h"
static void
submit_dns_answer(void *userdata, struct DNSReply *reply)
{
struct dns_request *req = userdata;
char response[64] = "*";
char status = 'E';
static void handle_lookup_ip_reply(void *data, struct DNSReply *reply);
static void handle_lookup_hostname_reply(void *data, struct DNSReply *reply);
if (reply == NULL)
uint64_t query_count = 0;
/* A bit different from ircd... you just get a dns_query object.
*
* It gets freed whenever the res code gets back to us.
*/
struct dns_query *
lookup_ip(const char *host, int aftype, DNSCB callback, void *data)
{
struct dns_query *query = rb_malloc(sizeof(struct dns_query));
int g_type;
if(aftype == AF_INET)
{
rb_helper_write(authd_helper, "E %s E %c *", req->reqid, req->type);
goto cleanup;
query->type = QUERY_A;
g_type = T_A;
}
#ifdef RB_IPV6
else if(aftype == AF_INET6)
{
query->type = QUERY_AAAA;
g_type = T_AAAA;
}
#endif
else
{
rb_free(query);
return NULL;
}
switch (req->type)
query->id = query_count++;
query->callback = callback;
query->data = data;
query->query.ptr = query;
query->query.callback = handle_lookup_ip_reply;
gethost_byname_type(host, &query->query, g_type);
return query;
}
/* See lookup_ip's comment */
struct dns_query *
lookup_hostname(const char *ip, int aftype, DNSCB callback, void *data)
{
struct dns_query *query = rb_malloc(sizeof(struct dns_query));
if(!rb_inet_pton_sock(ip, (struct sockaddr *)&query->addr))
{
case '4':
if (GET_SS_FAMILY(&reply->addr) == AF_INET)
{
status = 'O';
rb_inet_ntop_sock((struct sockaddr *) &reply->addr, response, sizeof(response));
}
break;
rb_free(query);
return NULL;
}
if(aftype == AF_INET)
query->type = QUERY_PTR_A;
#ifdef RB_IPV6
case '6':
if (GET_SS_FAMILY(&reply->addr) == AF_INET6)
{
char tmpres[63];
rb_inet_ntop_sock((struct sockaddr *) &reply->addr, tmpres, sizeof(tmpres));
if (*tmpres == ':')
{
rb_strlcpy(response, "0", sizeof(response));
rb_strlcat(response, tmpres, sizeof(response));
}
else
rb_strlcpy(response, tmpres, sizeof(response));
status = 'O';
}
break;
else if(aftype == AF_INET6)
query->type = QUERY_PTR_AAAA;
#endif
case 'R':
{
struct sockaddr_in *ip, *ip_fwd;
ip = (struct sockaddr_in *) &req->addr;
ip_fwd = (struct sockaddr_in *) &reply->addr;
else
{
rb_free(query);
return NULL;
}
if(ip->sin_addr.s_addr == ip_fwd->sin_addr.s_addr && strlen(reply->h_name) < 63)
{
rb_strlcpy(response, reply->h_name, sizeof(response));
status = 'O';
}
}
query->id = query_count++;
query->callback = callback;
query->data = data;
query->query.ptr = query;
query->query.callback = handle_lookup_hostname_reply;
gethost_byaddr(&query->addr, &query->query);
return query;
}
/* Callback from gethost_byname_type */
static void
handle_lookup_ip_reply(void *data, struct DNSReply *reply)
{
struct dns_query *query = data;
char ip[64] = "*";
query_type type = QUERY_INVALID;
if(!query)
/* Shouldn't happen */
exit(2);
type = query->type;
if(!reply)
goto end;
switch(query->type)
{
case QUERY_A:
if(GET_SS_FAMILY(&reply->addr) == AF_INET)
rb_inet_ntop_sock((struct sockaddr *)&reply->addr, ip, sizeof(ip));
break;
#ifdef RB_IPV6
case 'S':
case QUERY_AAAA:
if(GET_SS_FAMILY(&reply->addr) == AF_INET6)
{
struct sockaddr_in6 *ip, *ip_fwd;
ip = (struct sockaddr_in6 *) &req->addr;
ip_fwd = (struct sockaddr_in6 *) &reply->addr;
if(memcmp(&ip->sin6_addr, &ip_fwd->sin6_addr, sizeof(struct in6_addr)) == 0 && strlen(reply->h_name) < 63)
rb_inet_ntop_sock((struct sockaddr *)&reply->addr, ip, sizeof(ip));
if(ip[0] == ':')
{
rb_strlcpy(response, reply->h_name, sizeof(response));
status = 'O';
memmove(&ip[1], ip, strlen(ip));
ip[0] = '0';
}
}
break;
#endif
default:
exit(7);
exit(3);
}
rb_helper_write(authd_helper, "E %s %c %c %s", req->reqid, status, req->type, response);
cleanup:
rb_free(req);
end:
if(query->callback)
query->callback(ip, ip[0] != '*', type, query->data);
rb_free(query);
}
/* Callback from gethost_byaddr */
static void
handle_lookup_hostname_reply(void *data, struct DNSReply *reply)
{
struct dns_query *query = data;
char *hostname = NULL;
query_type type = QUERY_INVALID;
if(!query)
/* Shouldn't happen */
exit(4);
type = query->type;
if(!reply)
goto end;
if(query->type == QUERY_PTR_A)
{
struct sockaddr_in *ip, *ip_fwd;
ip = (struct sockaddr_in *) &query->addr;
ip_fwd = (struct sockaddr_in *) &reply->addr;
if(ip->sin_addr.s_addr == ip_fwd->sin_addr.s_addr && strlen(reply->h_name) < 63)
hostname = reply->h_name;
}
#ifdef RB_IPV6
else if(query->type == QUERY_PTR_AAAA)
{
struct sockaddr_in6 *ip, *ip_fwd;
ip = (struct sockaddr_in6 *) &query->addr;
ip_fwd = (struct sockaddr_in6 *) &reply->addr;
if(memcmp(&ip->sin6_addr, &ip_fwd->sin6_addr, sizeof(struct in6_addr)) == 0 && strlen(reply->h_name) < 63)
hostname = reply->h_name;
}
#endif
else
/* Shouldn't happen */
exit(5);
end:
if(query->callback)
query->callback(hostname, hostname != NULL, query->type, query->data);
rb_free(query);
}
static void
submit_dns_answer(const char *reply, bool status, query_type type, void *data)
{
char *id = data;
if(!id || type == QUERY_INVALID)
exit(6);
if(!reply || !status)
{
rb_helper_write(authd_helper, "E %s E %c *", id, type);
rb_free(id);
}
rb_helper_write(authd_helper, "E %s O %c %s", id, type, reply);
rb_free(id);
}
void
resolve_dns(int parc, char *parv[])
{
struct dns_request *req;
char *requestid = parv[1];
char *qtype = parv[2];
char *rec = parv[3];
int type;
char *id = rb_strdup(parv[1]);
char qtype = *parv[2];
char *record = parv[3];
int aftype = AF_INET;
req = rb_malloc(sizeof(*req));
rb_strlcpy(req->reqid, requestid, sizeof(req->reqid));
req->type = *qtype;
switch (req->type)
switch(qtype)
{
case '4':
type = T_A;
break;
#ifdef RB_IPV6
case '6':
type = T_AAAA;
aftype = AF_INET6;
#endif
case '4':
if(!lookup_ip(record, aftype, submit_dns_answer, id))
submit_dns_answer(NULL, false, qtype, NULL);
break;
case 'R':
#ifdef RB_IPV6
case 'S':
if(!rb_inet_pton_sock(rec, (struct sockaddr *) &req->addr))
exit(6);
type = T_PTR;
aftype = AF_INET6;
#endif
case 'R':
if(!lookup_hostname(record, aftype, submit_dns_answer, id))
submit_dns_answer(NULL, false, qtype, NULL);
break;
default:
exit(7);
}
req->query.ptr = req;
req->query.callback = submit_dns_answer;
if (type != T_PTR)
gethost_byname_type(rec, &req->query, type);
else
gethost_byaddr(&req->addr, &req->query);
}
void

View file

@ -23,17 +23,36 @@
#define DNS_REQ_IDLEN 10
#include "stdinc.h"
#include "res.h"
#include "reslib.h"
struct dns_request
typedef enum
{
QUERY_INVALID = 0,
QUERY_A = '4',
QUERY_AAAA = '6',
QUERY_PTR_A = 'R',
QUERY_PTR_AAAA = 'S',
} query_type;
/* Similar to that in ircd */
typedef void (*DNSCB)(const char *res, bool status, query_type type, void *data);
struct dns_query
{
struct DNSQuery query;
char reqid[DNS_REQ_IDLEN];
query_type type;
struct rb_sockaddr_storage addr;
char type;
uint64_t id;
DNSCB callback;
void *data;
};
extern struct dns_query *lookup_hostname(const char *ip, int aftype, DNSCB callback, void *data);
extern struct dns_query *lookup_ip(const char *host, int aftype, DNSCB callback, void *data);
extern void resolve_dns(int parc, char *parv[]);
extern void enumerate_nameservers(const char *rid, const char letter);
extern void reload_nameservers(const char letter);