From: Jelte Jansen Date: Wed, 23 Feb 2005 13:54:30 +0000 (+0000) Subject: Split up the tcp functionality in net.c X-Git-Tag: release-0.50~383 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9271177b0df11eece1978f07d720ef4432f09b35;p=thirdparty%2Fldns.git Split up the tcp functionality in net.c added run-test11, which is an example for an axfr transfer implementation test with ./run-test11 jelte.nlnetlabs.nl 195.169.215.155 --- diff --git a/Makefile.in b/Makefile.in index e85232b3..796eccc3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -43,7 +43,7 @@ LIBDNS_OBJECTS = $(LIBDNS_SOURCES:.c=.o) ALL_SOURCES = run-test0.c run-test1.c run-test2.c run-test3.c \ run-test4.c run-test5.c run-test6.c run-test7.c \ - run-test8.c run-test9.c \ + run-test8.c run-test9.c run-test10.c run-test11.c \ $(LIBDNS_SOURCES) TESTS = run-test0 \ @@ -56,7 +56,8 @@ TESTS = run-test0 \ run-test7 \ run-test8 \ run-test9 \ - run-test10 + run-test10 \ + run-test11 COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) COMP_LIB = $(LIBTOOL) $(CC) $(CPPFLAGS) $(CFLAGS) @@ -105,6 +106,8 @@ run-test9: run-test9.o $(LIBDNS_OBJECTS) $(LIBOBJS) $(LINK) ${LIBS} -o $@ $+ run-test10: run-test10.o $(LIBDNS_OBJECTS) $(LIBOBJS) $(LINK) ${LIBS} -o $@ $+ +run-test11: run-test11.o $(LIBDNS_OBJECTS) $(LIBOBJS) + $(LINK) ${LIBS} -o $@ $+ run-test-trace: run-test-trace.o $(LIBDNS_OBJECTS) $(LIBOBJS) $(LINK) ${LIBS} -o $@ $+ @@ -159,8 +162,10 @@ test9: run-test9 ./run-test9 test10: run-test10 ./run-test10 +test11: run-test11 + ./run-test11 -test: test0 test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 +test: test0 test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 ## No need for changes here diff --git a/ldns/net.h b/ldns/net.h index d8225317..c0505b6b 100644 --- a/ldns/net.h +++ b/ldns/net.h @@ -28,4 +28,8 @@ ldns_pkt * ldns_send_udp(ldns_buffer *, const struct sockaddr_storage *, socklen ldns_pkt * ldns_send_tcp(ldns_buffer *, const struct sockaddr_storage *, socklen_t); ldns_pkt * ldns_send(ldns_resolver *, ldns_pkt *); +int ldns_tcp_connect(const struct sockaddr_storage *to, socklen_t tolen); +ssize_t ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen); +ldns_pkt *ldns_tcp_read_packet(int sockfd); + #endif /* !_LDNS_NET_H */ diff --git a/net.c b/net.c index 35387aaa..cae7dbb8 100644 --- a/net.c +++ b/net.c @@ -33,16 +33,6 @@ #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 @@ -203,25 +193,13 @@ ldns_send_udp(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t to } /** - * 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; @@ -229,22 +207,32 @@ ldns_send_tcp(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t to 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)); @@ -258,68 +246,102 @@ ldns_send_tcp(ldns_buffer *qbin, const struct sockaddr_storage *to, socklen_t to 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; +} + diff --git a/run-test11.c b/run-test11.c new file mode 100644 index 00000000..06bbbe8b --- /dev/null +++ b/run-test11.c @@ -0,0 +1,142 @@ +/** + * An example ldns program + * + * Setup a resolver + * Query a nameserver + * Print the result + */ + +#include +#include +#include + +void +print_usage(char *file) +{ + printf("AXFR example\n"); + printf("Usage: %s \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; +}