]> git.ipfire.org Git - people/ms/libloc.git/blame - src/resolv.c
Make library build on Mac OS X
[people/ms/libloc.git] / src / resolv.c
CommitLineData
f4fef543
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2019 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
f7b7019a
MT
17#include <arpa/nameser.h>
18#include <arpa/nameser_compat.h>
f4fef543
MT
19#include <resolv.h>
20#include <string.h>
21#include <time.h>
22
23#include <loc/format.h>
24#include <loc/private.h>
25#include <loc/resolv.h>
26
27static int parse_timestamp(const unsigned char* txt, time_t* t) {
28 struct tm ts;
29
30 // Parse timestamp
31 char* p = strptime((const char*)txt, "%a, %d %b %Y %H:%M:%S GMT", &ts);
32
33 // If the whole string has been parsed, we convert the parse value to time_t
34 if (p && !*p) {
35 *t = mktime(&ts);
36
37 // Otherwise we reset t
38 } else {
39 *t = 0;
40 return -1;
41 }
42
43 return 0;
44}
45
46LOC_EXPORT int loc_discover_latest_version(struct loc_ctx* ctx, const char* domain, time_t* t) {
47 // Initialise the resolver
48 int r = res_init();
49 if (r) {
50 ERROR(ctx, "res_init() failed\n");
51 return r;
52 }
53
54 // Fall back to default domain
55 if (!domain)
56 domain = LOC_DATABASE_DOMAIN_LATEST(LOC_DATABASE_VERSION);
57
58 unsigned char answer[PACKETSZ];
59 int len;
60
61 DEBUG(ctx, "Querying %s\n", domain);
62
63 // Send a query
64 if ((len = res_query(domain, C_IN, T_TXT, answer, sizeof(answer))) < 0 || len > PACKETSZ) {
65 ERROR(ctx, "Could not query %s: \n", domain);
66
67 return -1;
68 }
69
70 unsigned char* end = answer + len;
71 unsigned char* payload = answer + sizeof(HEADER);
72
73 // Expand domain name
74 char host[128];
75 if ((len = dn_expand(answer, end, payload, host, sizeof(host))) < 0) {
76 ERROR(ctx, "dn_expand() failed\n");
77 return -1;
78 }
79
80 // Payload starts after hostname
81 payload += len;
82
83 if (payload > end - 4) {
84 ERROR(ctx, "DNS reply too short\n");
85 return -1;
86 }
87
88 int type;
89 GETSHORT(type, payload);
90 if (type != T_TXT) {
91 ERROR(ctx, "DNS reply of unexpected type: %d\n", type);
92 return -1;
93 }
94
95 // Skip class
96 payload += INT16SZ;
97
98 // Walk through CNAMEs
99 unsigned int size = 0;
100 int ttl;
101 do {
102 payload += size;
103
104 if ((len = dn_expand(answer, end, payload, host, sizeof(host))) < 0) {
105 ERROR(ctx, "dn_expand() failed\n");
106 return -1;
107 }
108
109 payload += len;
110
111 if (payload > end - 10) {
112 ERROR(ctx, "DNS reply too short\n");
113 return -1;
114 }
115
116 // Skip type, class, ttl
117 GETSHORT(type, payload);
118 payload += INT16SZ;
119 GETLONG(ttl, payload);
120
121 // Read size
122 GETSHORT(size, payload);
123 if (payload + size < answer || payload + size > end) {
124 ERROR(ctx, "DNS RR overflow\n");
125 return -1;
126 }
127 } while (type == T_CNAME);
128
129 if (type != T_TXT) {
130 ERROR(ctx, "Not a TXT record\n");
131 return -1;
132 }
133
134 if (!size || (len = *payload) >= size || !len) {
135 ERROR(ctx, "Broken TXT record (len = %d, size = %d)\n", len, size);
136 return -1;
137 }
138
139 // Get start of the string
140 unsigned char* txt = payload + 1;
141 txt[len] = '\0';
142
143 DEBUG(ctx, "Resolved to: %s\n", txt);
144
145 // Parse timestamp
146 r = parse_timestamp(txt, t);
147
148 return r;
149}