]> git.ipfire.org Git - location/libloc.git/blame - src/resolv.c
importer: Drop EDROP as it has been merged into DROP
[location/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
c12a1385
MT
23#include <libloc/format.h>
24#include <libloc/private.h>
25#include <libloc/resolv.h>
f4fef543
MT
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) {
22302d77 35 *t = timegm(&ts);
f4fef543
MT
36
37 // Otherwise we reset t
38 } else {
39 *t = 0;
40 return -1;
41 }
42
43 return 0;
44}
45
3ee36b5e
MT
46LOC_EXPORT int loc_discover_latest_version(struct loc_ctx* ctx,
47 unsigned int version, time_t* t) {
f4fef543
MT
48 // Initialise the resolver
49 int r = res_init();
50 if (r) {
51 ERROR(ctx, "res_init() failed\n");
52 return r;
53 }
54
3ee36b5e
MT
55 // Make domain
56 char domain[64];
57 snprintf(domain, 63, LOC_DATABASE_DOMAIN, version);
f4fef543
MT
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;
e069161b 101 int ttl __attribute__ ((unused));
f4fef543
MT
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}