#include "util.h"
-
-#if 0
-/* axfr is a hack - handle it different */
-ldns_pkt *
-ldns_sendbuf_axfr(ldns_buffer *buf, int *sockfd, struct sockaddr *dest)
-{
- return NULL;
-}
-#endif
-
/**
* Send to ptk to the nameserver at ipnumber. Return the data
* as a ldns_pkt
}
/**
- * Send a buffer to an ip using tcp and return the respons as a ldns_pkt
- * \param[in] qbin the ldns_buffer to be send
- * \param[in] to the ip addr to send to
- * \param[in] tolen length of the ip addr
- * \return a packet with the answer
+ * Create a tcp socket to the specified address
*/
-/* keep in mind that in DNS tcp messages the first 2 bytes signal the
- * amount data to expect
- */
-ldns_pkt *
-ldns_send_tcp(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen)
+int
+ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen)
{
int sockfd;
- ssize_t bytes, total_bytes;
- uint8_t *answer;
- ldns_pkt *answer_pkt;
- uint16_t answer_size;
- uint8_t *sendbuf;
-
+
struct timeval timeout;
timeout.tv_sec = LDNS_DEFAULT_TIMEOUT_SEC;
if ((sockfd = socket((int)((struct sockaddr*)to)->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("could not open socket");
- return NULL;
+ return 0;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
(socklen_t) sizeof(timeout))) {
perror("setsockopt");
close(sockfd);
- return NULL;
+ return 0;
}
if (connect(sockfd, (struct sockaddr*)to, tolen) == -1) {
close(sockfd);
perror("could not bind socket");
- return NULL;
+ return 0;
}
+ return sockfd;
+}
+
+
+ssize_t
+ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen)
+{
+ uint8_t *sendbuf;
+ ssize_t bytes;
+
/* add length of packet */
sendbuf = XMALLOC(uint8_t, ldns_buffer_position(qbin) + 2);
write_uint16(sendbuf, ldns_buffer_position(qbin));
if (bytes == -1) {
printf("error with sending\n");
close(sockfd);
- return NULL;
+ return 0;
}
if ((size_t) bytes != ldns_buffer_position(qbin)+2) {
printf("amount of sent bytes mismatch\n");
close(sockfd);
- return NULL;
+ return 0;
}
- /* wait for an response*/
- answer = XMALLOC(uint8_t, MAX_PACKETLEN);
- if (!answer) {
- printf("respons alloc error\n");
- return NULL;
- }
+ return bytes;
+}
- /* first two bytes are the size of the wiredata,
- we must be sure that we receive those */
- total_bytes = 0;
- while (total_bytes < 2) {
- bytes = recv(sockfd, answer, MAX_PACKETLEN, 0);
+/**
+ * Creates a new ldns_pkt structure and reads the header data from the given
+ * socket
+ */
+ldns_pkt *
+ldns_tcp_read_packet(int sockfd)
+{
+ ldns_pkt *pkt;
+ uint8_t *wire;
+ uint16_t wire_size;
+ ssize_t bytes = 0;
+
+ wire = XMALLOC(uint8_t, 2);
+ while (bytes < 2) {
+ bytes = recv(sockfd, wire, 2, 0);
if (bytes == -1) {
if (errno == EAGAIN) {
fprintf(stderr, "socket timeout\n");
}
perror("error receiving tcp packet");
- FREE(answer);
+ FREE(pkt);
return NULL;
- } else {
- total_bytes += bytes;
}
}
- answer_size = read_uint16(answer);
+ wire_size = read_uint16(wire);
- /* if we did not receive the whole packet in one tcp packet,
- we must recv() on */
- while (total_bytes < (ssize_t) (answer_size + 2)) {
- bytes = recv(sockfd, answer + total_bytes, (size_t) (MAX_PACKETLEN - total_bytes), 0);
+ FREE(wire);
+ wire = XMALLOC(uint8_t, wire_size);
+ bytes = 0;
+
+ while (bytes < (ssize_t) wire_size) {
+ bytes += recv(sockfd, wire + bytes, (size_t) (wire_size - bytes), 0);
if (bytes == -1) {
if (errno == EAGAIN) {
fprintf(stderr, "socket timeout\n");
}
perror("error receiving tcp packet");
- FREE(answer);
+ FREE(wire);
return NULL;
- } else {
- total_bytes += bytes;
}
}
- close(sockfd);
-
- /* resize accordingly */
- XREALLOC(answer, uint8_t *, (size_t) total_bytes);
-
- if (ldns_wire2pkt(&answer_pkt, answer+2, (size_t) answer_size) !=
+ if (ldns_wire2pkt(&pkt, wire, (size_t) wire_size) !=
LDNS_STATUS_OK) {
printf("could not create packet\n");
+ FREE(wire);
return NULL;
} else {
- ldns_pkt_set_size(answer_pkt, (size_t) bytes);
- return answer_pkt;
+ ldns_pkt_set_size(pkt, (size_t) bytes);
+ FREE(wire);
+ return pkt;
}
}
+/**
+ * Send a buffer to an ip using tcp and return the respons as a ldns_pkt
+ * \param[in] qbin the ldns_buffer to be send
+ * \param[in] to the ip addr to send to
+ * \param[in] tolen length of the ip addr
+ * \return a packet with the answer
+ */
+/* keep in mind that in DNS tcp messages the first 2 bytes signal the
+ * amount data to expect
+ */
+ldns_pkt *
+ldns_send_tcp(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t tolen)
+{
+ int sockfd;
+ ldns_pkt *answer;
+
+ sockfd = ldns_tcp_connect(to, tolen);
+
+ if (sockfd == 0) {
+ return NULL;
+ }
+
+ if (ldns_tcp_send_query(qbin, sockfd, to, tolen) == 0) {
+ return NULL;
+ }
+
+ answer = ldns_tcp_read_packet(sockfd);
+
+ close(sockfd);
+
+ return answer;
+}
+
--- /dev/null
+/**
+ * An example ldns program
+ *
+ * Setup a resolver
+ * Query a nameserver
+ * Print the result
+ */
+
+#include <config.h>
+#include <ldns/ldns.h>
+#include <ldns/dname.h>
+
+void
+print_usage(char *file)
+{
+ printf("AXFR example\n");
+ printf("Usage: %s <domain> <server ip>\n", file);
+ printf("ipv4 only atm\n");
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ ldns_rdf *nameserver;
+
+ ldns_pkt *query;
+ ldns_buffer *query_wire;
+
+ ldns_pkt *pkt;
+ int soa_count;
+ int connection;
+
+ struct sockaddr_storage *ns;
+ struct sockaddr_in *ns4;
+ struct sockaddr_in6 *ns6;
+ int ns_len = 0;
+
+ char *server_ip = NULL;
+ char *name = NULL;
+
+ ldns_rr_list *rr_list;
+ ldns_rr *cur_rr;
+ char *rr_str;
+ uint16_t i;
+
+ /* Get the domain and the nameserver from the command line */
+ if (argc < 3) {
+ print_usage(argv[0]);
+ } else {
+ name = argv[1];
+ server_ip = argv[2];
+ }
+
+ nameserver = ldns_rdf_new_frm_str(server_ip, LDNS_RDF_TYPE_A);
+ if (!nameserver) {
+ printf("Bad server ip\n");
+ return -1;
+ }
+
+ /* Create the query */
+ query = ldns_pkt_query_new_frm_str(name,
+ LDNS_RR_TYPE_AXFR,
+ LDNS_RR_CLASS_IN,
+ 0);
+
+ /* For AXFR, we have to make the connection ourselves */
+ ns = ldns_rdf2native_sockaddr_storage(nameserver);
+
+ ldns_rdf_free(nameserver);
+
+ /* Determine the address size.
+ * This is a nice one for a convenience funtion
+ */
+ switch(ns->ss_family) {
+ case AF_INET:
+ ns4 = (struct sockaddr_in*) ns;
+ ns4->sin_port = htons(53);
+ ns_len = (socklen_t)sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ ns6 = (struct sockaddr_in6*) ns;
+ ns6->sin6_port = htons(53);
+ ns_len = (socklen_t)sizeof(struct sockaddr_in6);
+ break;
+ default:
+ printf("unkown inet family\n");
+ return -1;
+ }
+
+ connection = ldns_tcp_connect(ns, ns_len);
+ if (connection == 0) {
+ return -1;
+ }
+
+ /* Convert the query to a buffer
+ * Is this necessary?
+ */
+ query_wire = ldns_buffer_new(MAX_PACKETLEN);
+ if (ldns_pkt2buffer_wire(query_wire, query) != LDNS_STATUS_OK) {
+ printf("Unable to create wire data for query\n");
+ return -1;
+ }
+
+ /* Send the query */
+ ldns_tcp_send_query(query_wire, connection, ns, ns_len);
+
+ ldns_pkt_free(query);
+ ldns_buffer_free(query_wire);
+
+ /* Print all the resource records we receive.
+ * The AXFR is done once the second SOA record is sent
+ */
+ soa_count = 0;
+ while (soa_count < 2) {
+ pkt = ldns_tcp_read_packet(connection);
+
+ if (!pkt) {
+ printf("error reading packet\n");
+ } else {
+ rr_list = ldns_pkt_answer(pkt);
+
+ /* Counting the number of certain types of rrs might
+ * be another good convenience function */
+ for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
+ cur_rr = ldns_rr_list_rr(rr_list, i);
+ if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_SOA) {
+ soa_count++;
+ }
+ rr_str = ldns_rr2str(cur_rr);
+ printf("%s\n", rr_str);
+ FREE(rr_str);
+ }
+ ldns_pkt_free(pkt);
+ }
+ }
+
+ /* Don't forget to close the connection */
+ close(connection);
+
+ return 0;
+}