]> git.ipfire.org Git - location/libloc.git/blob - src/resolv.c
importer: Drop EDROP as it has been merged into DROP
[location/libloc.git] / src / resolv.c
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
17 #include <arpa/nameser.h>
18 #include <arpa/nameser_compat.h>
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
27 static 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 = timegm(&ts);
36
37 // Otherwise we reset t
38 } else {
39 *t = 0;
40 return -1;
41 }
42
43 return 0;
44 }
45
46 LOC_EXPORT int loc_discover_latest_version(struct loc_ctx* ctx,
47 unsigned int version, time_t* t) {
48 // Initialise the resolver
49 int r = res_init();
50 if (r) {
51 ERROR(ctx, "res_init() failed\n");
52 return r;
53 }
54
55 // Make domain
56 char domain[64];
57 snprintf(domain, 63, LOC_DATABASE_DOMAIN, version);
58
59 unsigned char answer[PACKETSZ];
60 int len;
61
62 DEBUG(ctx, "Querying %s\n", domain);
63
64 // Send a query
65 if ((len = res_query(domain, C_IN, T_TXT, answer, sizeof(answer))) < 0 || len > PACKETSZ) {
66 ERROR(ctx, "Could not query %s: \n", domain);
67
68 return -1;
69 }
70
71 unsigned char* end = answer + len;
72 unsigned char* payload = answer + sizeof(HEADER);
73
74 // Expand domain name
75 char host[128];
76 if ((len = dn_expand(answer, end, payload, host, sizeof(host))) < 0) {
77 ERROR(ctx, "dn_expand() failed\n");
78 return -1;
79 }
80
81 // Payload starts after hostname
82 payload += len;
83
84 if (payload > end - 4) {
85 ERROR(ctx, "DNS reply too short\n");
86 return -1;
87 }
88
89 int type;
90 GETSHORT(type, payload);
91 if (type != T_TXT) {
92 ERROR(ctx, "DNS reply of unexpected type: %d\n", type);
93 return -1;
94 }
95
96 // Skip class
97 payload += INT16SZ;
98
99 // Walk through CNAMEs
100 unsigned int size = 0;
101 int ttl __attribute__ ((unused));
102 do {
103 payload += size;
104
105 if ((len = dn_expand(answer, end, payload, host, sizeof(host))) < 0) {
106 ERROR(ctx, "dn_expand() failed\n");
107 return -1;
108 }
109
110 payload += len;
111
112 if (payload > end - 10) {
113 ERROR(ctx, "DNS reply too short\n");
114 return -1;
115 }
116
117 // Skip type, class, ttl
118 GETSHORT(type, payload);
119 payload += INT16SZ;
120 GETLONG(ttl, payload);
121
122 // Read size
123 GETSHORT(size, payload);
124 if (payload + size < answer || payload + size > end) {
125 ERROR(ctx, "DNS RR overflow\n");
126 return -1;
127 }
128 } while (type == T_CNAME);
129
130 if (type != T_TXT) {
131 ERROR(ctx, "Not a TXT record\n");
132 return -1;
133 }
134
135 if (!size || (len = *payload) >= size || !len) {
136 ERROR(ctx, "Broken TXT record (len = %d, size = %d)\n", len, size);
137 return -1;
138 }
139
140 // Get start of the string
141 unsigned char* txt = payload + 1;
142 txt[len] = '\0';
143
144 DEBUG(ctx, "Resolved to: %s\n", txt);
145
146 // Parse timestamp
147 r = parse_timestamp(txt, t);
148
149 return r;
150 }