xref: /freebsd/contrib/unbound/smallapp/unbound-anchor.c (revision b7579f77d18196a58ff700756c84dc9a302a7f67)
1*b7579f77SDag-Erling Smørgrav /*
2*b7579f77SDag-Erling Smørgrav  * unbound-anchor.c - update the root anchor if necessary.
3*b7579f77SDag-Erling Smørgrav  *
4*b7579f77SDag-Erling Smørgrav  * Copyright (c) 2010, NLnet Labs. All rights reserved.
5*b7579f77SDag-Erling Smørgrav  *
6*b7579f77SDag-Erling Smørgrav  * This software is open source.
7*b7579f77SDag-Erling Smørgrav  *
8*b7579f77SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
9*b7579f77SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
10*b7579f77SDag-Erling Smørgrav  * are met:
11*b7579f77SDag-Erling Smørgrav  *
12*b7579f77SDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
13*b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
14*b7579f77SDag-Erling Smørgrav  *
15*b7579f77SDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
16*b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
17*b7579f77SDag-Erling Smørgrav  * and/or other materials provided with the distribution.
18*b7579f77SDag-Erling Smørgrav  *
19*b7579f77SDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
20*b7579f77SDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
21*b7579f77SDag-Erling Smørgrav  * specific prior written permission.
22*b7579f77SDag-Erling Smørgrav  *
23*b7579f77SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*b7579f77SDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25*b7579f77SDag-Erling Smørgrav  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26*b7579f77SDag-Erling Smørgrav  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27*b7579f77SDag-Erling Smørgrav  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28*b7579f77SDag-Erling Smørgrav  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29*b7579f77SDag-Erling Smørgrav  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30*b7579f77SDag-Erling Smørgrav  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31*b7579f77SDag-Erling Smørgrav  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32*b7579f77SDag-Erling Smørgrav  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33*b7579f77SDag-Erling Smørgrav  * POSSIBILITY OF SUCH DAMAGE.
34*b7579f77SDag-Erling Smørgrav  */
35*b7579f77SDag-Erling Smørgrav 
36*b7579f77SDag-Erling Smørgrav /**
37*b7579f77SDag-Erling Smørgrav  * \file
38*b7579f77SDag-Erling Smørgrav  *
39*b7579f77SDag-Erling Smørgrav  * This file checks to see that the current 5011 keys work to prime the
40*b7579f77SDag-Erling Smørgrav  * current root anchor.  If not a certificate is used to update the anchor.
41*b7579f77SDag-Erling Smørgrav  *
42*b7579f77SDag-Erling Smørgrav  * This is a concept solution for distribution of the DNSSEC root
43*b7579f77SDag-Erling Smørgrav  * trust anchor.  It is a small tool, called "unbound-anchor", that
44*b7579f77SDag-Erling Smørgrav  * runs before the main validator starts.  I.e. in the init script:
45*b7579f77SDag-Erling Smørgrav  * unbound-anchor; unbound.  Thus it is meant to run at system boot time.
46*b7579f77SDag-Erling Smørgrav  *
47*b7579f77SDag-Erling Smørgrav  * Management-Abstract:
48*b7579f77SDag-Erling Smørgrav  *    * first run: fill root.key file with hardcoded DS record.
49*b7579f77SDag-Erling Smørgrav  *    * mostly: use RFC5011 tracking, quick . DNSKEY UDP query.
50*b7579f77SDag-Erling Smørgrav  *    * failover: use builtin certificate, do https and update.
51*b7579f77SDag-Erling Smørgrav  * Special considerations:
52*b7579f77SDag-Erling Smørgrav  *    * 30-days RFC5011 timer saves a lot of https traffic.
53*b7579f77SDag-Erling Smørgrav  *    * DNSKEY probe must be NOERROR, saves a lot of https traffic.
54*b7579f77SDag-Erling Smørgrav  *    * fail if clock before sign date of the root, if cert expired.
55*b7579f77SDag-Erling Smørgrav  *    * if the root goes back to unsigned, deals with it.
56*b7579f77SDag-Erling Smørgrav  *
57*b7579f77SDag-Erling Smørgrav  * It has hardcoded the root DS anchors and the ICANN CA root certificate.
58*b7579f77SDag-Erling Smørgrav  * It allows with options to override those.  It also takes root-hints (it
59*b7579f77SDag-Erling Smørgrav  * has to do a DNS resolve), and also has hardcoded defaults for those.
60*b7579f77SDag-Erling Smørgrav  *
61*b7579f77SDag-Erling Smørgrav  * Once it starts, just before the validator starts, it quickly checks if
62*b7579f77SDag-Erling Smørgrav  * the root anchor file needs to be updated.  First it tries to use
63*b7579f77SDag-Erling Smørgrav  * RFC5011-tracking of the root key.  If that fails (and for 30-days since
64*b7579f77SDag-Erling Smørgrav  * last successful probe), then it attempts to update using the
65*b7579f77SDag-Erling Smørgrav  * certificate.  So most of the time, the RFC5011 tracking will work fine,
66*b7579f77SDag-Erling Smørgrav  * and within a couple milliseconds, the main daemon can start.  It will
67*b7579f77SDag-Erling Smørgrav  * have only probed the . DNSKEY, not done expensive https transfers on the
68*b7579f77SDag-Erling Smørgrav  * root infrastructure.
69*b7579f77SDag-Erling Smørgrav  *
70*b7579f77SDag-Erling Smørgrav  * If there is no root key in the root.key file, it bootstraps the
71*b7579f77SDag-Erling Smørgrav  * RFC5011-tracking with its builtin DS anchors; if that fails it
72*b7579f77SDag-Erling Smørgrav  * bootstraps the RFC5011-tracking using the certificate.  (again to avoid
73*b7579f77SDag-Erling Smørgrav  * https, and it is also faster).
74*b7579f77SDag-Erling Smørgrav  *
75*b7579f77SDag-Erling Smørgrav  * It uses the XML file by converting it to DS records and writing that to the
76*b7579f77SDag-Erling Smørgrav  * key file.  Unbound can detect that the 'special comments' are gone, and
77*b7579f77SDag-Erling Smørgrav  * the file contains a list of normal DNSKEY/DS records, and uses that to
78*b7579f77SDag-Erling Smørgrav  * bootstrap 5011 (the KSK is made VALID).
79*b7579f77SDag-Erling Smørgrav  *
80*b7579f77SDag-Erling Smørgrav  * The certificate update is done by fetching root-anchors.xml and
81*b7579f77SDag-Erling Smørgrav  * root-anchors.p7s via SSL.  The HTTPS certificate can be logged but is
82*b7579f77SDag-Erling Smørgrav  * not validated (https for channel security; the security comes from the
83*b7579f77SDag-Erling Smørgrav  * certificate).  The 'data.iana.org' domain name A and AAAA are resolved
84*b7579f77SDag-Erling Smørgrav  * without DNSSEC.  It tries a random IP until the transfer succeeds.  It
85*b7579f77SDag-Erling Smørgrav  * then checks the p7s signature.
86*b7579f77SDag-Erling Smørgrav  *
87*b7579f77SDag-Erling Smørgrav  * On any failure, it leaves the root key file untouched.  The main
88*b7579f77SDag-Erling Smørgrav  * validator has to cope with it, it cannot fix things (So a failure does
89*b7579f77SDag-Erling Smørgrav  * not go 'without DNSSEC', no downgrade).  If it used its builtin stuff or
90*b7579f77SDag-Erling Smørgrav  * did the https, it exits with an exit code, so that this can trigger the
91*b7579f77SDag-Erling Smørgrav  * init script to log the event and potentially alert the operator that can
92*b7579f77SDag-Erling Smørgrav  * do a manual check.
93*b7579f77SDag-Erling Smørgrav  *
94*b7579f77SDag-Erling Smørgrav  * The date is also checked.  Before 2010-07-15 is a failure (root not
95*b7579f77SDag-Erling Smørgrav  * signed yet; avoids attacks on system clock).  The
96*b7579f77SDag-Erling Smørgrav  * last-successful-RFC5011-probe (if available) has to be more than 30 days
97*b7579f77SDag-Erling Smørgrav  * in the past (otherwise, RFC5011 should have worked).  This keeps
98*b7579f77SDag-Erling Smørgrav  * unneccesary https traffic down.  If the main certificate is expired, it
99*b7579f77SDag-Erling Smørgrav  * fails.
100*b7579f77SDag-Erling Smørgrav  *
101*b7579f77SDag-Erling Smørgrav  * The dates on the keys in the xml are checked (uses the libexpat xml
102*b7579f77SDag-Erling Smørgrav  * parser), only the valid ones are used to re-enstate RFC5011 tracking.
103*b7579f77SDag-Erling Smørgrav  * If 0 keys are valid, the zone has gone to insecure (a special marker is
104*b7579f77SDag-Erling Smørgrav  * written in the keyfile that tells the main validator daemon the zone is
105*b7579f77SDag-Erling Smørgrav  * insecure).
106*b7579f77SDag-Erling Smørgrav  *
107*b7579f77SDag-Erling Smørgrav  * Only the root ICANN CA is shipped, not the intermediate ones.  The
108*b7579f77SDag-Erling Smørgrav  * intermediate CAs are included in the p7s file that was downloaded.  (the
109*b7579f77SDag-Erling Smørgrav  * root cert is valid to 2028 and the intermediate to 2014, today).
110*b7579f77SDag-Erling Smørgrav  *
111*b7579f77SDag-Erling Smørgrav  * Obviously, the tool also has options so the operator can provide a new
112*b7579f77SDag-Erling Smørgrav  * keyfile, a new certificate and new URLs, and fresh root hints.  By
113*b7579f77SDag-Erling Smørgrav  * default it logs nothing on failure and success; it 'just works'.
114*b7579f77SDag-Erling Smørgrav  *
115*b7579f77SDag-Erling Smørgrav  */
116*b7579f77SDag-Erling Smørgrav 
117*b7579f77SDag-Erling Smørgrav #include "config.h"
118*b7579f77SDag-Erling Smørgrav #include "libunbound/unbound.h"
119*b7579f77SDag-Erling Smørgrav #include <ldns/rr.h>
120*b7579f77SDag-Erling Smørgrav #include <expat.h>
121*b7579f77SDag-Erling Smørgrav #ifndef HAVE_EXPAT_H
122*b7579f77SDag-Erling Smørgrav #error "need libexpat to parse root-anchors.xml file."
123*b7579f77SDag-Erling Smørgrav #endif
124*b7579f77SDag-Erling Smørgrav #ifdef HAVE_GETOPT_H
125*b7579f77SDag-Erling Smørgrav #include <getopt.h>
126*b7579f77SDag-Erling Smørgrav #endif
127*b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_SSL_H
128*b7579f77SDag-Erling Smørgrav #include <openssl/ssl.h>
129*b7579f77SDag-Erling Smørgrav #endif
130*b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_ERR_H
131*b7579f77SDag-Erling Smørgrav #include <openssl/err.h>
132*b7579f77SDag-Erling Smørgrav #endif
133*b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_RAND_H
134*b7579f77SDag-Erling Smørgrav #include <openssl/rand.h>
135*b7579f77SDag-Erling Smørgrav #endif
136*b7579f77SDag-Erling Smørgrav #include <openssl/x509.h>
137*b7579f77SDag-Erling Smørgrav #include <openssl/pem.h>
138*b7579f77SDag-Erling Smørgrav 
139*b7579f77SDag-Erling Smørgrav /** name of server in URL to fetch HTTPS from */
140*b7579f77SDag-Erling Smørgrav #define URLNAME "data.iana.org"
141*b7579f77SDag-Erling Smørgrav /** path on HTTPS server to xml file */
142*b7579f77SDag-Erling Smørgrav #define XMLNAME "root-anchors/root-anchors.xml"
143*b7579f77SDag-Erling Smørgrav /** path on HTTPS server to p7s file */
144*b7579f77SDag-Erling Smørgrav #define P7SNAME "root-anchors/root-anchors.p7s"
145*b7579f77SDag-Erling Smørgrav /** port number for https access */
146*b7579f77SDag-Erling Smørgrav #define HTTPS_PORT 443
147*b7579f77SDag-Erling Smørgrav 
148*b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK
149*b7579f77SDag-Erling Smørgrav /* sneakily reuse the the wsa_strerror function, on windows */
150*b7579f77SDag-Erling Smørgrav char* wsa_strerror(int err);
151*b7579f77SDag-Erling Smørgrav #endif
152*b7579f77SDag-Erling Smørgrav 
153*b7579f77SDag-Erling Smørgrav /** verbosity for this application */
154*b7579f77SDag-Erling Smørgrav static int verb = 0;
155*b7579f77SDag-Erling Smørgrav 
156*b7579f77SDag-Erling Smørgrav /** list of IP addresses */
157*b7579f77SDag-Erling Smørgrav struct ip_list {
158*b7579f77SDag-Erling Smørgrav 	/** next in list */
159*b7579f77SDag-Erling Smørgrav 	struct ip_list* next;
160*b7579f77SDag-Erling Smørgrav 	/** length of addr */
161*b7579f77SDag-Erling Smørgrav 	socklen_t len;
162*b7579f77SDag-Erling Smørgrav 	/** address ready to connect to */
163*b7579f77SDag-Erling Smørgrav 	struct sockaddr_storage addr;
164*b7579f77SDag-Erling Smørgrav 	/** has the address been used */
165*b7579f77SDag-Erling Smørgrav 	int used;
166*b7579f77SDag-Erling Smørgrav };
167*b7579f77SDag-Erling Smørgrav 
168*b7579f77SDag-Erling Smørgrav /** Give unbound-anchor usage, and exit (1). */
169*b7579f77SDag-Erling Smørgrav static void
170*b7579f77SDag-Erling Smørgrav usage()
171*b7579f77SDag-Erling Smørgrav {
172*b7579f77SDag-Erling Smørgrav 	printf("Usage:	unbound-anchor [opts]\n");
173*b7579f77SDag-Erling Smørgrav 	printf("	Setup or update root anchor. "
174*b7579f77SDag-Erling Smørgrav 		"Most options have defaults.\n");
175*b7579f77SDag-Erling Smørgrav 	printf("	Run this program before you start the validator.\n");
176*b7579f77SDag-Erling Smørgrav 	printf("\n");
177*b7579f77SDag-Erling Smørgrav 	printf("	The anchor and cert have default builtin content\n");
178*b7579f77SDag-Erling Smørgrav 	printf("	if the file does not exist or is empty.\n");
179*b7579f77SDag-Erling Smørgrav 	printf("\n");
180*b7579f77SDag-Erling Smørgrav 	printf("-a file		root key file, default %s\n", ROOT_ANCHOR_FILE);
181*b7579f77SDag-Erling Smørgrav 	printf("		The key is input and output for this tool.\n");
182*b7579f77SDag-Erling Smørgrav 	printf("-c file		cert file, default %s\n", ROOT_CERT_FILE);
183*b7579f77SDag-Erling Smørgrav 	printf("-l		list builtin key and cert on stdout\n");
184*b7579f77SDag-Erling Smørgrav 	printf("-u name		server in https url, default %s\n", URLNAME);
185*b7579f77SDag-Erling Smørgrav 	printf("-x path		pathname to xml in url, default %s\n", XMLNAME);
186*b7579f77SDag-Erling Smørgrav 	printf("-s path		pathname to p7s in url, default %s\n", P7SNAME);
187*b7579f77SDag-Erling Smørgrav 	printf("-4		work using IPv4 only\n");
188*b7579f77SDag-Erling Smørgrav 	printf("-6		work using IPv6 only\n");
189*b7579f77SDag-Erling Smørgrav 	printf("-f resolv.conf	use given resolv.conf to resolve -u name\n");
190*b7579f77SDag-Erling Smørgrav 	printf("-r root.hints	use given root.hints to resolve -u name\n"
191*b7579f77SDag-Erling Smørgrav 		"		builtin root hints are used by default\n");
192*b7579f77SDag-Erling Smørgrav 	printf("-v		more verbose\n");
193*b7579f77SDag-Erling Smørgrav 	printf("-C conf		debug, read config\n");
194*b7579f77SDag-Erling Smørgrav 	printf("-P port		use port for https connect, default 443\n");
195*b7579f77SDag-Erling Smørgrav 	printf("-F 		debug, force update with cert\n");
196*b7579f77SDag-Erling Smørgrav 	printf("-h		show this usage help\n");
197*b7579f77SDag-Erling Smørgrav 	printf("Version %s\n", PACKAGE_VERSION);
198*b7579f77SDag-Erling Smørgrav 	printf("BSD licensed, see LICENSE in source package for details.\n");
199*b7579f77SDag-Erling Smørgrav 	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
200*b7579f77SDag-Erling Smørgrav 	exit(1);
201*b7579f77SDag-Erling Smørgrav }
202*b7579f77SDag-Erling Smørgrav 
203*b7579f77SDag-Erling Smørgrav /** return the built in root update certificate */
204*b7579f77SDag-Erling Smørgrav static const char*
205*b7579f77SDag-Erling Smørgrav get_builtin_cert(void)
206*b7579f77SDag-Erling Smørgrav {
207*b7579f77SDag-Erling Smørgrav 	return
208*b7579f77SDag-Erling Smørgrav /* The ICANN CA fetched at 24 Sep 2010.  Valid to 2028 */
209*b7579f77SDag-Erling Smørgrav "-----BEGIN CERTIFICATE-----\n"
210*b7579f77SDag-Erling Smørgrav "MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n"
211*b7579f77SDag-Erling Smørgrav "TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n"
212*b7579f77SDag-Erling Smørgrav "BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n"
213*b7579f77SDag-Erling Smørgrav "DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n"
214*b7579f77SDag-Erling Smørgrav "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n"
215*b7579f77SDag-Erling Smørgrav "MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n"
216*b7579f77SDag-Erling Smørgrav "cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n"
217*b7579f77SDag-Erling Smørgrav "G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n"
218*b7579f77SDag-Erling Smørgrav "ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n"
219*b7579f77SDag-Erling Smørgrav "paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n"
220*b7579f77SDag-Erling Smørgrav "MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n"
221*b7579f77SDag-Erling Smørgrav "iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
222*b7579f77SDag-Erling Smørgrav "Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n"
223*b7579f77SDag-Erling Smørgrav "DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n"
224*b7579f77SDag-Erling Smørgrav "6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n"
225*b7579f77SDag-Erling Smørgrav "2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n"
226*b7579f77SDag-Erling Smørgrav "15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n"
227*b7579f77SDag-Erling Smørgrav "0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n"
228*b7579f77SDag-Erling Smørgrav "j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n"
229*b7579f77SDag-Erling Smørgrav "-----END CERTIFICATE-----\n"
230*b7579f77SDag-Erling Smørgrav 		;
231*b7579f77SDag-Erling Smørgrav }
232*b7579f77SDag-Erling Smørgrav 
233*b7579f77SDag-Erling Smørgrav /** return the built in root DS trust anchor */
234*b7579f77SDag-Erling Smørgrav static const char*
235*b7579f77SDag-Erling Smørgrav get_builtin_ds(void)
236*b7579f77SDag-Erling Smørgrav {
237*b7579f77SDag-Erling Smørgrav 	return
238*b7579f77SDag-Erling Smørgrav ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n";
239*b7579f77SDag-Erling Smørgrav }
240*b7579f77SDag-Erling Smørgrav 
241*b7579f77SDag-Erling Smørgrav /** print hex data */
242*b7579f77SDag-Erling Smørgrav static void
243*b7579f77SDag-Erling Smørgrav print_data(char* msg, char* data, int len)
244*b7579f77SDag-Erling Smørgrav {
245*b7579f77SDag-Erling Smørgrav 	int i;
246*b7579f77SDag-Erling Smørgrav 	printf("%s: ", msg);
247*b7579f77SDag-Erling Smørgrav 	for(i=0; i<len; i++) {
248*b7579f77SDag-Erling Smørgrav 		printf(" %2.2x", (unsigned char)data[i]);
249*b7579f77SDag-Erling Smørgrav 	}
250*b7579f77SDag-Erling Smørgrav 	printf("\n");
251*b7579f77SDag-Erling Smørgrav }
252*b7579f77SDag-Erling Smørgrav 
253*b7579f77SDag-Erling Smørgrav /** print ub context creation error and exit */
254*b7579f77SDag-Erling Smørgrav static void
255*b7579f77SDag-Erling Smørgrav ub_ctx_error_exit(struct ub_ctx* ctx, const char* str, const char* str2)
256*b7579f77SDag-Erling Smørgrav {
257*b7579f77SDag-Erling Smørgrav 	ub_ctx_delete(ctx);
258*b7579f77SDag-Erling Smørgrav 	if(str && str2 && verb) printf("%s: %s\n", str, str2);
259*b7579f77SDag-Erling Smørgrav 	if(verb) printf("error: could not create unbound resolver context\n");
260*b7579f77SDag-Erling Smørgrav 	exit(0);
261*b7579f77SDag-Erling Smørgrav }
262*b7579f77SDag-Erling Smørgrav 
263*b7579f77SDag-Erling Smørgrav /**
264*b7579f77SDag-Erling Smørgrav  * Create a new unbound context with the commandline settings applied
265*b7579f77SDag-Erling Smørgrav  */
266*b7579f77SDag-Erling Smørgrav static struct ub_ctx*
267*b7579f77SDag-Erling Smørgrav create_unbound_context(char* res_conf, char* root_hints, char* debugconf,
268*b7579f77SDag-Erling Smørgrav         int ip4only, int ip6only)
269*b7579f77SDag-Erling Smørgrav {
270*b7579f77SDag-Erling Smørgrav 	int r;
271*b7579f77SDag-Erling Smørgrav 	struct ub_ctx* ctx = ub_ctx_create();
272*b7579f77SDag-Erling Smørgrav 	if(!ctx) {
273*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
274*b7579f77SDag-Erling Smørgrav 		exit(0);
275*b7579f77SDag-Erling Smørgrav 	}
276*b7579f77SDag-Erling Smørgrav 	/* do not waste time and network traffic to fetch extra nameservers */
277*b7579f77SDag-Erling Smørgrav 	r = ub_ctx_set_option(ctx, "target-fetch-policy:", "0 0 0 0 0");
278*b7579f77SDag-Erling Smørgrav 	if(r && verb) printf("ctx targetfetchpolicy: %s\n", ub_strerror(r));
279*b7579f77SDag-Erling Smørgrav 	/* read config file first, so its settings can be overridden */
280*b7579f77SDag-Erling Smørgrav 	if(debugconf) {
281*b7579f77SDag-Erling Smørgrav 		r = ub_ctx_config(ctx, debugconf);
282*b7579f77SDag-Erling Smørgrav 		if(r) ub_ctx_error_exit(ctx, debugconf, ub_strerror(r));
283*b7579f77SDag-Erling Smørgrav 	}
284*b7579f77SDag-Erling Smørgrav 	if(res_conf) {
285*b7579f77SDag-Erling Smørgrav 		r = ub_ctx_resolvconf(ctx, res_conf);
286*b7579f77SDag-Erling Smørgrav 		if(r) ub_ctx_error_exit(ctx, res_conf, ub_strerror(r));
287*b7579f77SDag-Erling Smørgrav 	}
288*b7579f77SDag-Erling Smørgrav 	if(root_hints) {
289*b7579f77SDag-Erling Smørgrav 		r = ub_ctx_set_option(ctx, "root-hints:", root_hints);
290*b7579f77SDag-Erling Smørgrav 		if(r) ub_ctx_error_exit(ctx, root_hints, ub_strerror(r));
291*b7579f77SDag-Erling Smørgrav 	}
292*b7579f77SDag-Erling Smørgrav 	if(ip4only) {
293*b7579f77SDag-Erling Smørgrav 		r = ub_ctx_set_option(ctx, "do-ip6:", "no");
294*b7579f77SDag-Erling Smørgrav 		if(r) ub_ctx_error_exit(ctx, "ip4only", ub_strerror(r));
295*b7579f77SDag-Erling Smørgrav 	}
296*b7579f77SDag-Erling Smørgrav 	if(ip6only) {
297*b7579f77SDag-Erling Smørgrav 		r = ub_ctx_set_option(ctx, "do-ip4:", "no");
298*b7579f77SDag-Erling Smørgrav 		if(r) ub_ctx_error_exit(ctx, "ip6only", ub_strerror(r));
299*b7579f77SDag-Erling Smørgrav 	}
300*b7579f77SDag-Erling Smørgrav 	return ctx;
301*b7579f77SDag-Erling Smørgrav }
302*b7579f77SDag-Erling Smørgrav 
303*b7579f77SDag-Erling Smørgrav /** printout certificate in detail */
304*b7579f77SDag-Erling Smørgrav static void
305*b7579f77SDag-Erling Smørgrav verb_cert(char* msg, X509* x)
306*b7579f77SDag-Erling Smørgrav {
307*b7579f77SDag-Erling Smørgrav 	if(verb == 0 || verb == 1) return;
308*b7579f77SDag-Erling Smørgrav 	if(verb == 2) {
309*b7579f77SDag-Erling Smørgrav 		if(msg) printf("%s\n", msg);
310*b7579f77SDag-Erling Smørgrav 		X509_print_ex_fp(stdout, x, 0, (unsigned long)-1
311*b7579f77SDag-Erling Smørgrav 			^(X509_FLAG_NO_SUBJECT
312*b7579f77SDag-Erling Smørgrav 			|X509_FLAG_NO_ISSUER|X509_FLAG_NO_VALIDITY));
313*b7579f77SDag-Erling Smørgrav 		return;
314*b7579f77SDag-Erling Smørgrav 	}
315*b7579f77SDag-Erling Smørgrav 	if(msg) printf("%s\n", msg);
316*b7579f77SDag-Erling Smørgrav 	X509_print_fp(stdout, x);
317*b7579f77SDag-Erling Smørgrav }
318*b7579f77SDag-Erling Smørgrav 
319*b7579f77SDag-Erling Smørgrav /** printout certificates in detail */
320*b7579f77SDag-Erling Smørgrav static void
321*b7579f77SDag-Erling Smørgrav verb_certs(char* msg, STACK_OF(X509)* sk)
322*b7579f77SDag-Erling Smørgrav {
323*b7579f77SDag-Erling Smørgrav 	int i, num = sk_X509_num(sk);
324*b7579f77SDag-Erling Smørgrav 	if(verb == 0 || verb == 1) return;
325*b7579f77SDag-Erling Smørgrav 	for(i=0; i<num; i++) {
326*b7579f77SDag-Erling Smørgrav 		printf("%s (%d/%d)\n", msg, i, num);
327*b7579f77SDag-Erling Smørgrav 		verb_cert(NULL, sk_X509_value(sk, i));
328*b7579f77SDag-Erling Smørgrav 	}
329*b7579f77SDag-Erling Smørgrav }
330*b7579f77SDag-Erling Smørgrav 
331*b7579f77SDag-Erling Smørgrav /** read certificates from a PEM bio */
332*b7579f77SDag-Erling Smørgrav static STACK_OF(X509)*
333*b7579f77SDag-Erling Smørgrav read_cert_bio(BIO* bio)
334*b7579f77SDag-Erling Smørgrav {
335*b7579f77SDag-Erling Smørgrav 	STACK_OF(X509) *sk = sk_X509_new_null();
336*b7579f77SDag-Erling Smørgrav 	if(!sk) {
337*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
338*b7579f77SDag-Erling Smørgrav 		exit(0);
339*b7579f77SDag-Erling Smørgrav 	}
340*b7579f77SDag-Erling Smørgrav 	while(!BIO_eof(bio)) {
341*b7579f77SDag-Erling Smørgrav 		X509* x = PEM_read_bio_X509(bio, NULL, 0, NULL);
342*b7579f77SDag-Erling Smørgrav 		if(x == NULL) {
343*b7579f77SDag-Erling Smørgrav 			if(verb) {
344*b7579f77SDag-Erling Smørgrav 				printf("failed to read X509\n");
345*b7579f77SDag-Erling Smørgrav 			 	ERR_print_errors_fp(stdout);
346*b7579f77SDag-Erling Smørgrav 			}
347*b7579f77SDag-Erling Smørgrav 			continue;
348*b7579f77SDag-Erling Smørgrav 		}
349*b7579f77SDag-Erling Smørgrav 		if(!sk_X509_push(sk, x)) {
350*b7579f77SDag-Erling Smørgrav 			if(verb) printf("out of memory\n");
351*b7579f77SDag-Erling Smørgrav 			exit(0);
352*b7579f77SDag-Erling Smørgrav 		}
353*b7579f77SDag-Erling Smørgrav 	}
354*b7579f77SDag-Erling Smørgrav 	return sk;
355*b7579f77SDag-Erling Smørgrav }
356*b7579f77SDag-Erling Smørgrav 
357*b7579f77SDag-Erling Smørgrav /* read the certificate file */
358*b7579f77SDag-Erling Smørgrav static STACK_OF(X509)*
359*b7579f77SDag-Erling Smørgrav read_cert_file(char* file)
360*b7579f77SDag-Erling Smørgrav {
361*b7579f77SDag-Erling Smørgrav 	STACK_OF(X509)* sk;
362*b7579f77SDag-Erling Smørgrav 	FILE* in;
363*b7579f77SDag-Erling Smørgrav 	int content = 0;
364*b7579f77SDag-Erling Smørgrav 	char buf[128];
365*b7579f77SDag-Erling Smørgrav 	if(file == NULL || strcmp(file, "") == 0) {
366*b7579f77SDag-Erling Smørgrav 		return NULL;
367*b7579f77SDag-Erling Smørgrav 	}
368*b7579f77SDag-Erling Smørgrav 	sk = sk_X509_new_null();
369*b7579f77SDag-Erling Smørgrav 	if(!sk) {
370*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
371*b7579f77SDag-Erling Smørgrav 		exit(0);
372*b7579f77SDag-Erling Smørgrav 	}
373*b7579f77SDag-Erling Smørgrav 	in = fopen(file, "r");
374*b7579f77SDag-Erling Smørgrav 	if(!in) {
375*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s: %s\n", file, strerror(errno));
376*b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S
377*b7579f77SDag-Erling Smørgrav 		sk_X509_pop_free(sk, X509_free);
378*b7579f77SDag-Erling Smørgrav #endif
379*b7579f77SDag-Erling Smørgrav 		return NULL;
380*b7579f77SDag-Erling Smørgrav 	}
381*b7579f77SDag-Erling Smørgrav 	while(!feof(in)) {
382*b7579f77SDag-Erling Smørgrav 		X509* x = PEM_read_X509(in, NULL, 0, NULL);
383*b7579f77SDag-Erling Smørgrav 		if(x == NULL) {
384*b7579f77SDag-Erling Smørgrav 			if(verb) {
385*b7579f77SDag-Erling Smørgrav 				printf("failed to read X509 file\n");
386*b7579f77SDag-Erling Smørgrav 			 	ERR_print_errors_fp(stdout);
387*b7579f77SDag-Erling Smørgrav 			}
388*b7579f77SDag-Erling Smørgrav 			continue;
389*b7579f77SDag-Erling Smørgrav 		}
390*b7579f77SDag-Erling Smørgrav 		if(!sk_X509_push(sk, x)) {
391*b7579f77SDag-Erling Smørgrav 			if(verb) printf("out of memory\n");
392*b7579f77SDag-Erling Smørgrav 			fclose(in);
393*b7579f77SDag-Erling Smørgrav 			exit(0);
394*b7579f77SDag-Erling Smørgrav 		}
395*b7579f77SDag-Erling Smørgrav 		content = 1;
396*b7579f77SDag-Erling Smørgrav 		/* read away newline after --END CERT-- */
397*b7579f77SDag-Erling Smørgrav 		if(!fgets(buf, (int)sizeof(buf), in))
398*b7579f77SDag-Erling Smørgrav 			break;
399*b7579f77SDag-Erling Smørgrav 	}
400*b7579f77SDag-Erling Smørgrav 	fclose(in);
401*b7579f77SDag-Erling Smørgrav 	if(!content) {
402*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s is empty\n", file);
403*b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S
404*b7579f77SDag-Erling Smørgrav 		sk_X509_pop_free(sk, X509_free);
405*b7579f77SDag-Erling Smørgrav #endif
406*b7579f77SDag-Erling Smørgrav 		return NULL;
407*b7579f77SDag-Erling Smørgrav 	}
408*b7579f77SDag-Erling Smørgrav 	return sk;
409*b7579f77SDag-Erling Smørgrav }
410*b7579f77SDag-Erling Smørgrav 
411*b7579f77SDag-Erling Smørgrav /** read certificates from the builtin certificate */
412*b7579f77SDag-Erling Smørgrav static STACK_OF(X509)*
413*b7579f77SDag-Erling Smørgrav read_builtin_cert(void)
414*b7579f77SDag-Erling Smørgrav {
415*b7579f77SDag-Erling Smørgrav 	const char* builtin_cert = get_builtin_cert();
416*b7579f77SDag-Erling Smørgrav 	STACK_OF(X509)* sk;
417*b7579f77SDag-Erling Smørgrav 	BIO *bio = BIO_new_mem_buf((void*)builtin_cert,
418*b7579f77SDag-Erling Smørgrav 		(int)strlen(builtin_cert));
419*b7579f77SDag-Erling Smørgrav 	if(!bio) {
420*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
421*b7579f77SDag-Erling Smørgrav 		exit(0);
422*b7579f77SDag-Erling Smørgrav 	}
423*b7579f77SDag-Erling Smørgrav 	sk = read_cert_bio(bio);
424*b7579f77SDag-Erling Smørgrav 	if(!sk) {
425*b7579f77SDag-Erling Smørgrav 		if(verb) printf("internal error, out of memory\n");
426*b7579f77SDag-Erling Smørgrav 		exit(0);
427*b7579f77SDag-Erling Smørgrav 	}
428*b7579f77SDag-Erling Smørgrav 	BIO_free(bio);
429*b7579f77SDag-Erling Smørgrav 	return sk;
430*b7579f77SDag-Erling Smørgrav }
431*b7579f77SDag-Erling Smørgrav 
432*b7579f77SDag-Erling Smørgrav /** read update cert file or use builtin */
433*b7579f77SDag-Erling Smørgrav static STACK_OF(X509)*
434*b7579f77SDag-Erling Smørgrav read_cert_or_builtin(char* file)
435*b7579f77SDag-Erling Smørgrav {
436*b7579f77SDag-Erling Smørgrav 	STACK_OF(X509) *sk = read_cert_file(file);
437*b7579f77SDag-Erling Smørgrav 	if(!sk) {
438*b7579f77SDag-Erling Smørgrav 		if(verb) printf("using builtin certificate\n");
439*b7579f77SDag-Erling Smørgrav 		sk = read_builtin_cert();
440*b7579f77SDag-Erling Smørgrav 	}
441*b7579f77SDag-Erling Smørgrav 	if(verb) printf("have %d trusted certificates\n", sk_X509_num(sk));
442*b7579f77SDag-Erling Smørgrav 	verb_certs("trusted certificates", sk);
443*b7579f77SDag-Erling Smørgrav 	return sk;
444*b7579f77SDag-Erling Smørgrav }
445*b7579f77SDag-Erling Smørgrav 
446*b7579f77SDag-Erling Smørgrav static void
447*b7579f77SDag-Erling Smørgrav do_list_builtin(void)
448*b7579f77SDag-Erling Smørgrav {
449*b7579f77SDag-Erling Smørgrav 	const char* builtin_cert = get_builtin_cert();
450*b7579f77SDag-Erling Smørgrav 	const char* builtin_ds = get_builtin_ds();
451*b7579f77SDag-Erling Smørgrav 	printf("%s\n", builtin_ds);
452*b7579f77SDag-Erling Smørgrav 	printf("%s\n", builtin_cert);
453*b7579f77SDag-Erling Smørgrav 	exit(0);
454*b7579f77SDag-Erling Smørgrav }
455*b7579f77SDag-Erling Smørgrav 
456*b7579f77SDag-Erling Smørgrav /** printout IP address with message */
457*b7579f77SDag-Erling Smørgrav static void
458*b7579f77SDag-Erling Smørgrav verb_addr(char* msg, struct ip_list* ip)
459*b7579f77SDag-Erling Smørgrav {
460*b7579f77SDag-Erling Smørgrav 	if(verb) {
461*b7579f77SDag-Erling Smørgrav 		char out[100];
462*b7579f77SDag-Erling Smørgrav 		void* a = &((struct sockaddr_in*)&ip->addr)->sin_addr;
463*b7579f77SDag-Erling Smørgrav 		if(ip->len != (socklen_t)sizeof(struct sockaddr_in))
464*b7579f77SDag-Erling Smørgrav 			a = &((struct sockaddr_in6*)&ip->addr)->sin6_addr;
465*b7579f77SDag-Erling Smørgrav 
466*b7579f77SDag-Erling Smørgrav 		if(inet_ntop((int)((struct sockaddr_in*)&ip->addr)->sin_family,
467*b7579f77SDag-Erling Smørgrav 			a, out, (socklen_t)sizeof(out))==0)
468*b7579f77SDag-Erling Smørgrav 			printf("%s (inet_ntop error)\n", msg);
469*b7579f77SDag-Erling Smørgrav 		else printf("%s %s\n", msg, out);
470*b7579f77SDag-Erling Smørgrav 	}
471*b7579f77SDag-Erling Smørgrav }
472*b7579f77SDag-Erling Smørgrav 
473*b7579f77SDag-Erling Smørgrav /** free ip_list */
474*b7579f77SDag-Erling Smørgrav static void
475*b7579f77SDag-Erling Smørgrav ip_list_free(struct ip_list* p)
476*b7579f77SDag-Erling Smørgrav {
477*b7579f77SDag-Erling Smørgrav 	struct ip_list* np;
478*b7579f77SDag-Erling Smørgrav 	while(p) {
479*b7579f77SDag-Erling Smørgrav 		np = p->next;
480*b7579f77SDag-Erling Smørgrav 		free(p);
481*b7579f77SDag-Erling Smørgrav 		p = np;
482*b7579f77SDag-Erling Smørgrav 	}
483*b7579f77SDag-Erling Smørgrav }
484*b7579f77SDag-Erling Smørgrav 
485*b7579f77SDag-Erling Smørgrav /** create ip_list entry for a RR record */
486*b7579f77SDag-Erling Smørgrav static struct ip_list*
487*b7579f77SDag-Erling Smørgrav RR_to_ip(int tp, char* data, int len, int port)
488*b7579f77SDag-Erling Smørgrav {
489*b7579f77SDag-Erling Smørgrav 	struct ip_list* ip = (struct ip_list*)calloc(1, sizeof(*ip));
490*b7579f77SDag-Erling Smørgrav 	uint16_t p = (uint16_t)port;
491*b7579f77SDag-Erling Smørgrav 	if(tp == LDNS_RR_TYPE_A) {
492*b7579f77SDag-Erling Smørgrav 		struct sockaddr_in* sa = (struct sockaddr_in*)&ip->addr;
493*b7579f77SDag-Erling Smørgrav 		ip->len = (socklen_t)sizeof(*sa);
494*b7579f77SDag-Erling Smørgrav 		sa->sin_family = AF_INET;
495*b7579f77SDag-Erling Smørgrav 		sa->sin_port = (in_port_t)htons(p);
496*b7579f77SDag-Erling Smørgrav 		if(len != (int)sizeof(sa->sin_addr)) {
497*b7579f77SDag-Erling Smørgrav 			if(verb) printf("skipped badly formatted A\n");
498*b7579f77SDag-Erling Smørgrav 			free(ip);
499*b7579f77SDag-Erling Smørgrav 			return NULL;
500*b7579f77SDag-Erling Smørgrav 		}
501*b7579f77SDag-Erling Smørgrav 		memmove(&sa->sin_addr, data, sizeof(sa->sin_addr));
502*b7579f77SDag-Erling Smørgrav 
503*b7579f77SDag-Erling Smørgrav 	} else if(tp == LDNS_RR_TYPE_AAAA) {
504*b7579f77SDag-Erling Smørgrav 		struct sockaddr_in6* sa = (struct sockaddr_in6*)&ip->addr;
505*b7579f77SDag-Erling Smørgrav 		ip->len = (socklen_t)sizeof(*sa);
506*b7579f77SDag-Erling Smørgrav 		sa->sin6_family = AF_INET6;
507*b7579f77SDag-Erling Smørgrav 		sa->sin6_port = (in_port_t)htons(p);
508*b7579f77SDag-Erling Smørgrav 		if(len != (int)sizeof(sa->sin6_addr)) {
509*b7579f77SDag-Erling Smørgrav 			if(verb) printf("skipped badly formatted AAAA\n");
510*b7579f77SDag-Erling Smørgrav 			free(ip);
511*b7579f77SDag-Erling Smørgrav 			return NULL;
512*b7579f77SDag-Erling Smørgrav 		}
513*b7579f77SDag-Erling Smørgrav 		memmove(&sa->sin6_addr, data, sizeof(sa->sin6_addr));
514*b7579f77SDag-Erling Smørgrav 	} else {
515*b7579f77SDag-Erling Smørgrav 		if(verb) printf("internal error: bad type in RRtoip\n");
516*b7579f77SDag-Erling Smørgrav 		free(ip);
517*b7579f77SDag-Erling Smørgrav 		return NULL;
518*b7579f77SDag-Erling Smørgrav 	}
519*b7579f77SDag-Erling Smørgrav 	verb_addr("resolved server address", ip);
520*b7579f77SDag-Erling Smørgrav 	return ip;
521*b7579f77SDag-Erling Smørgrav }
522*b7579f77SDag-Erling Smørgrav 
523*b7579f77SDag-Erling Smørgrav /** Resolve name, type, class and add addresses to iplist */
524*b7579f77SDag-Erling Smørgrav static void
525*b7579f77SDag-Erling Smørgrav resolve_host_ip(struct ub_ctx* ctx, char* host, int port, int tp, int cl,
526*b7579f77SDag-Erling Smørgrav 	struct ip_list** head)
527*b7579f77SDag-Erling Smørgrav {
528*b7579f77SDag-Erling Smørgrav 	struct ub_result* res = NULL;
529*b7579f77SDag-Erling Smørgrav 	int r;
530*b7579f77SDag-Erling Smørgrav 	int i;
531*b7579f77SDag-Erling Smørgrav 
532*b7579f77SDag-Erling Smørgrav 	r = ub_resolve(ctx, host, tp, cl, &res);
533*b7579f77SDag-Erling Smørgrav 	if(r) {
534*b7579f77SDag-Erling Smørgrav 		if(verb) printf("error: resolve %s %s: %s\n", host,
535*b7579f77SDag-Erling Smørgrav 			(tp==LDNS_RR_TYPE_A)?"A":"AAAA", ub_strerror(r));
536*b7579f77SDag-Erling Smørgrav 		return;
537*b7579f77SDag-Erling Smørgrav 	}
538*b7579f77SDag-Erling Smørgrav 	if(!res) {
539*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
540*b7579f77SDag-Erling Smørgrav 		ub_ctx_delete(ctx);
541*b7579f77SDag-Erling Smørgrav 		exit(0);
542*b7579f77SDag-Erling Smørgrav 	}
543*b7579f77SDag-Erling Smørgrav 	for(i = 0; res->data[i]; i++) {
544*b7579f77SDag-Erling Smørgrav 		struct ip_list* ip = RR_to_ip(tp, res->data[i], res->len[i],
545*b7579f77SDag-Erling Smørgrav 			port);
546*b7579f77SDag-Erling Smørgrav 		if(!ip) continue;
547*b7579f77SDag-Erling Smørgrav 		ip->next = *head;
548*b7579f77SDag-Erling Smørgrav 		*head = ip;
549*b7579f77SDag-Erling Smørgrav 	}
550*b7579f77SDag-Erling Smørgrav 	ub_resolve_free(res);
551*b7579f77SDag-Erling Smørgrav }
552*b7579f77SDag-Erling Smørgrav 
553*b7579f77SDag-Erling Smørgrav /** parse a text IP address into a sockaddr */
554*b7579f77SDag-Erling Smørgrav static struct ip_list*
555*b7579f77SDag-Erling Smørgrav parse_ip_addr(char* str, int port)
556*b7579f77SDag-Erling Smørgrav {
557*b7579f77SDag-Erling Smørgrav 	socklen_t len = 0;
558*b7579f77SDag-Erling Smørgrav 	struct sockaddr_storage* addr = NULL;
559*b7579f77SDag-Erling Smørgrav 	struct sockaddr_in6 a6;
560*b7579f77SDag-Erling Smørgrav 	struct sockaddr_in a;
561*b7579f77SDag-Erling Smørgrav 	struct ip_list* ip;
562*b7579f77SDag-Erling Smørgrav 	uint16_t p = (uint16_t)port;
563*b7579f77SDag-Erling Smørgrav 	memset(&a6, 0, sizeof(a6));
564*b7579f77SDag-Erling Smørgrav 	memset(&a, 0, sizeof(a));
565*b7579f77SDag-Erling Smørgrav 
566*b7579f77SDag-Erling Smørgrav 	if(inet_pton(AF_INET6, str, &a6.sin6_addr) > 0) {
567*b7579f77SDag-Erling Smørgrav 		/* it is an IPv6 */
568*b7579f77SDag-Erling Smørgrav 		a6.sin6_family = AF_INET6;
569*b7579f77SDag-Erling Smørgrav 		a6.sin6_port = (in_port_t)htons(p);
570*b7579f77SDag-Erling Smørgrav 		addr = (struct sockaddr_storage*)&a6;
571*b7579f77SDag-Erling Smørgrav 		len = (socklen_t)sizeof(struct sockaddr_in6);
572*b7579f77SDag-Erling Smørgrav 	}
573*b7579f77SDag-Erling Smørgrav 	if(inet_pton(AF_INET, str, &a.sin_addr) > 0) {
574*b7579f77SDag-Erling Smørgrav 		/* it is an IPv4 */
575*b7579f77SDag-Erling Smørgrav 		a.sin_family = AF_INET;
576*b7579f77SDag-Erling Smørgrav 		a.sin_port = (in_port_t)htons(p);
577*b7579f77SDag-Erling Smørgrav 		addr = (struct sockaddr_storage*)&a;
578*b7579f77SDag-Erling Smørgrav 		len = (socklen_t)sizeof(struct sockaddr_in);
579*b7579f77SDag-Erling Smørgrav 	}
580*b7579f77SDag-Erling Smørgrav 	if(!len) return NULL;
581*b7579f77SDag-Erling Smørgrav 	ip = (struct ip_list*)calloc(1, sizeof(*ip));
582*b7579f77SDag-Erling Smørgrav 	if(!ip) {
583*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
584*b7579f77SDag-Erling Smørgrav 		exit(0);
585*b7579f77SDag-Erling Smørgrav 	}
586*b7579f77SDag-Erling Smørgrav 	ip->len = len;
587*b7579f77SDag-Erling Smørgrav 	memmove(&ip->addr, addr, len);
588*b7579f77SDag-Erling Smørgrav 	if(verb) printf("server address is %s\n", str);
589*b7579f77SDag-Erling Smørgrav 	return ip;
590*b7579f77SDag-Erling Smørgrav }
591*b7579f77SDag-Erling Smørgrav 
592*b7579f77SDag-Erling Smørgrav /**
593*b7579f77SDag-Erling Smørgrav  * Resolve a domain name (even though the resolver is down and there is
594*b7579f77SDag-Erling Smørgrav  * no trust anchor).  Without DNSSEC validation.
595*b7579f77SDag-Erling Smørgrav  * @param host: the name to resolve.
596*b7579f77SDag-Erling Smørgrav  * 	If this name is an IP4 or IP6 address this address is returned.
597*b7579f77SDag-Erling Smørgrav  * @param port: the port number used for the returned IP structs.
598*b7579f77SDag-Erling Smørgrav  * @param res_conf: resolv.conf (if any).
599*b7579f77SDag-Erling Smørgrav  * @param root_hints: root hints (if any).
600*b7579f77SDag-Erling Smørgrav  * @param debugconf: unbound.conf for debugging options.
601*b7579f77SDag-Erling Smørgrav  * @param ip4only: use only ip4 for resolve and only lookup A
602*b7579f77SDag-Erling Smørgrav  * @param ip6only: use only ip6 for resolve and only lookup AAAA
603*b7579f77SDag-Erling Smørgrav  * 	default is to lookup A and AAAA using ip4 and ip6.
604*b7579f77SDag-Erling Smørgrav  * @return list of IP addresses.
605*b7579f77SDag-Erling Smørgrav  */
606*b7579f77SDag-Erling Smørgrav static struct ip_list*
607*b7579f77SDag-Erling Smørgrav resolve_name(char* host, int port, char* res_conf, char* root_hints,
608*b7579f77SDag-Erling Smørgrav 	char* debugconf, int ip4only, int ip6only)
609*b7579f77SDag-Erling Smørgrav {
610*b7579f77SDag-Erling Smørgrav 	struct ub_ctx* ctx;
611*b7579f77SDag-Erling Smørgrav 	struct ip_list* list = NULL;
612*b7579f77SDag-Erling Smørgrav 	/* first see if name is an IP address itself */
613*b7579f77SDag-Erling Smørgrav 	if( (list=parse_ip_addr(host, port)) ) {
614*b7579f77SDag-Erling Smørgrav 		return list;
615*b7579f77SDag-Erling Smørgrav 	}
616*b7579f77SDag-Erling Smørgrav 
617*b7579f77SDag-Erling Smørgrav 	/* create resolver context */
618*b7579f77SDag-Erling Smørgrav 	ctx = create_unbound_context(res_conf, root_hints, debugconf,
619*b7579f77SDag-Erling Smørgrav         	ip4only, ip6only);
620*b7579f77SDag-Erling Smørgrav 
621*b7579f77SDag-Erling Smørgrav 	/* try resolution of A */
622*b7579f77SDag-Erling Smørgrav 	if(!ip6only) {
623*b7579f77SDag-Erling Smørgrav 		resolve_host_ip(ctx, host, port, LDNS_RR_TYPE_A,
624*b7579f77SDag-Erling Smørgrav 			LDNS_RR_CLASS_IN, &list);
625*b7579f77SDag-Erling Smørgrav 	}
626*b7579f77SDag-Erling Smørgrav 
627*b7579f77SDag-Erling Smørgrav 	/* try resolution of AAAA */
628*b7579f77SDag-Erling Smørgrav 	if(!ip4only) {
629*b7579f77SDag-Erling Smørgrav 		resolve_host_ip(ctx, host, port, LDNS_RR_TYPE_AAAA,
630*b7579f77SDag-Erling Smørgrav 			LDNS_RR_CLASS_IN, &list);
631*b7579f77SDag-Erling Smørgrav 	}
632*b7579f77SDag-Erling Smørgrav 
633*b7579f77SDag-Erling Smørgrav 	ub_ctx_delete(ctx);
634*b7579f77SDag-Erling Smørgrav 	if(!list) {
635*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s has no IP addresses I can use\n", host);
636*b7579f77SDag-Erling Smørgrav 		exit(0);
637*b7579f77SDag-Erling Smørgrav 	}
638*b7579f77SDag-Erling Smørgrav 	return list;
639*b7579f77SDag-Erling Smørgrav }
640*b7579f77SDag-Erling Smørgrav 
641*b7579f77SDag-Erling Smørgrav /** clear used flags */
642*b7579f77SDag-Erling Smørgrav static void
643*b7579f77SDag-Erling Smørgrav wipe_ip_usage(struct ip_list* p)
644*b7579f77SDag-Erling Smørgrav {
645*b7579f77SDag-Erling Smørgrav 	while(p) {
646*b7579f77SDag-Erling Smørgrav 		p->used = 0;
647*b7579f77SDag-Erling Smørgrav 		p = p->next;
648*b7579f77SDag-Erling Smørgrav 	}
649*b7579f77SDag-Erling Smørgrav }
650*b7579f77SDag-Erling Smørgrav 
651*b7579f77SDag-Erling Smørgrav /** cound unused IPs */
652*b7579f77SDag-Erling Smørgrav static int
653*b7579f77SDag-Erling Smørgrav count_unused(struct ip_list* p)
654*b7579f77SDag-Erling Smørgrav {
655*b7579f77SDag-Erling Smørgrav 	int num = 0;
656*b7579f77SDag-Erling Smørgrav 	while(p) {
657*b7579f77SDag-Erling Smørgrav 		if(!p->used) num++;
658*b7579f77SDag-Erling Smørgrav 		p = p->next;
659*b7579f77SDag-Erling Smørgrav 	}
660*b7579f77SDag-Erling Smørgrav 	return num;
661*b7579f77SDag-Erling Smørgrav }
662*b7579f77SDag-Erling Smørgrav 
663*b7579f77SDag-Erling Smørgrav /** pick random unused element from IP list */
664*b7579f77SDag-Erling Smørgrav static struct ip_list*
665*b7579f77SDag-Erling Smørgrav pick_random_ip(struct ip_list* list)
666*b7579f77SDag-Erling Smørgrav {
667*b7579f77SDag-Erling Smørgrav 	struct ip_list* p = list;
668*b7579f77SDag-Erling Smørgrav 	int num = count_unused(list);
669*b7579f77SDag-Erling Smørgrav 	int sel;
670*b7579f77SDag-Erling Smørgrav 	if(num == 0) return NULL;
671*b7579f77SDag-Erling Smørgrav 	/* not perfect, but random enough */
672*b7579f77SDag-Erling Smørgrav 	sel = (int)ldns_get_random() % num;
673*b7579f77SDag-Erling Smørgrav 	/* skip over unused elements that we did not select */
674*b7579f77SDag-Erling Smørgrav 	while(sel > 0 && p) {
675*b7579f77SDag-Erling Smørgrav 		if(!p->used) sel--;
676*b7579f77SDag-Erling Smørgrav 		p = p->next;
677*b7579f77SDag-Erling Smørgrav 	}
678*b7579f77SDag-Erling Smørgrav 	/* find the next unused element */
679*b7579f77SDag-Erling Smørgrav 	while(p && p->used)
680*b7579f77SDag-Erling Smørgrav 		p = p->next;
681*b7579f77SDag-Erling Smørgrav 	if(!p) return NULL; /* robustness */
682*b7579f77SDag-Erling Smørgrav 	return p;
683*b7579f77SDag-Erling Smørgrav }
684*b7579f77SDag-Erling Smørgrav 
685*b7579f77SDag-Erling Smørgrav /** close the fd */
686*b7579f77SDag-Erling Smørgrav static void
687*b7579f77SDag-Erling Smørgrav fd_close(int fd)
688*b7579f77SDag-Erling Smørgrav {
689*b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK
690*b7579f77SDag-Erling Smørgrav 	close(fd);
691*b7579f77SDag-Erling Smørgrav #else
692*b7579f77SDag-Erling Smørgrav 	closesocket(fd);
693*b7579f77SDag-Erling Smørgrav #endif
694*b7579f77SDag-Erling Smørgrav }
695*b7579f77SDag-Erling Smørgrav 
696*b7579f77SDag-Erling Smørgrav /** printout socket errno */
697*b7579f77SDag-Erling Smørgrav static void
698*b7579f77SDag-Erling Smørgrav print_sock_err(const char* msg)
699*b7579f77SDag-Erling Smørgrav {
700*b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK
701*b7579f77SDag-Erling Smørgrav 	if(verb) printf("%s: %s\n", msg, strerror(errno));
702*b7579f77SDag-Erling Smørgrav #else
703*b7579f77SDag-Erling Smørgrav 	if(verb) printf("%s: %s\n", msg, wsa_strerror(WSAGetLastError()));
704*b7579f77SDag-Erling Smørgrav #endif
705*b7579f77SDag-Erling Smørgrav }
706*b7579f77SDag-Erling Smørgrav 
707*b7579f77SDag-Erling Smørgrav /** connect to IP address */
708*b7579f77SDag-Erling Smørgrav static int
709*b7579f77SDag-Erling Smørgrav connect_to_ip(struct ip_list* ip)
710*b7579f77SDag-Erling Smørgrav {
711*b7579f77SDag-Erling Smørgrav 	int fd;
712*b7579f77SDag-Erling Smørgrav 	verb_addr("connect to", ip);
713*b7579f77SDag-Erling Smørgrav 	fd = socket(ip->len==(socklen_t)sizeof(struct sockaddr_in)?
714*b7579f77SDag-Erling Smørgrav 		AF_INET:AF_INET6, SOCK_STREAM, 0);
715*b7579f77SDag-Erling Smørgrav 	if(fd == -1) {
716*b7579f77SDag-Erling Smørgrav 		print_sock_err("socket");
717*b7579f77SDag-Erling Smørgrav 		return -1;
718*b7579f77SDag-Erling Smørgrav 	}
719*b7579f77SDag-Erling Smørgrav 	if(connect(fd, (struct sockaddr*)&ip->addr, ip->len) < 0) {
720*b7579f77SDag-Erling Smørgrav 		print_sock_err("connect");
721*b7579f77SDag-Erling Smørgrav 		fd_close(fd);
722*b7579f77SDag-Erling Smørgrav 		return -1;
723*b7579f77SDag-Erling Smørgrav 	}
724*b7579f77SDag-Erling Smørgrav 	return fd;
725*b7579f77SDag-Erling Smørgrav }
726*b7579f77SDag-Erling Smørgrav 
727*b7579f77SDag-Erling Smørgrav /** create SSL context */
728*b7579f77SDag-Erling Smørgrav static SSL_CTX*
729*b7579f77SDag-Erling Smørgrav setup_sslctx(void)
730*b7579f77SDag-Erling Smørgrav {
731*b7579f77SDag-Erling Smørgrav 	SSL_CTX* sslctx = SSL_CTX_new(SSLv23_client_method());
732*b7579f77SDag-Erling Smørgrav 	if(!sslctx) {
733*b7579f77SDag-Erling Smørgrav 		if(verb) printf("SSL_CTX_new error\n");
734*b7579f77SDag-Erling Smørgrav 		return NULL;
735*b7579f77SDag-Erling Smørgrav 	}
736*b7579f77SDag-Erling Smørgrav 	return sslctx;
737*b7579f77SDag-Erling Smørgrav }
738*b7579f77SDag-Erling Smørgrav 
739*b7579f77SDag-Erling Smørgrav /** initiate TLS on a connection */
740*b7579f77SDag-Erling Smørgrav static SSL*
741*b7579f77SDag-Erling Smørgrav TLS_initiate(SSL_CTX* sslctx, int fd)
742*b7579f77SDag-Erling Smørgrav {
743*b7579f77SDag-Erling Smørgrav 	X509* x;
744*b7579f77SDag-Erling Smørgrav 	int r;
745*b7579f77SDag-Erling Smørgrav 	SSL* ssl = SSL_new(sslctx);
746*b7579f77SDag-Erling Smørgrav 	if(!ssl) {
747*b7579f77SDag-Erling Smørgrav 		if(verb) printf("SSL_new error\n");
748*b7579f77SDag-Erling Smørgrav 		return NULL;
749*b7579f77SDag-Erling Smørgrav 	}
750*b7579f77SDag-Erling Smørgrav 	SSL_set_connect_state(ssl);
751*b7579f77SDag-Erling Smørgrav 	(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
752*b7579f77SDag-Erling Smørgrav 	if(!SSL_set_fd(ssl, fd)) {
753*b7579f77SDag-Erling Smørgrav 		if(verb) printf("SSL_set_fd error\n");
754*b7579f77SDag-Erling Smørgrav 		SSL_free(ssl);
755*b7579f77SDag-Erling Smørgrav 		return NULL;
756*b7579f77SDag-Erling Smørgrav 	}
757*b7579f77SDag-Erling Smørgrav 	while(1) {
758*b7579f77SDag-Erling Smørgrav 		ERR_clear_error();
759*b7579f77SDag-Erling Smørgrav 		if( (r=SSL_do_handshake(ssl)) == 1)
760*b7579f77SDag-Erling Smørgrav 			break;
761*b7579f77SDag-Erling Smørgrav 		r = SSL_get_error(ssl, r);
762*b7579f77SDag-Erling Smørgrav 		if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) {
763*b7579f77SDag-Erling Smørgrav 			if(verb) printf("SSL handshake failed\n");
764*b7579f77SDag-Erling Smørgrav 			SSL_free(ssl);
765*b7579f77SDag-Erling Smørgrav 			return NULL;
766*b7579f77SDag-Erling Smørgrav 		}
767*b7579f77SDag-Erling Smørgrav 		/* wants to be called again */
768*b7579f77SDag-Erling Smørgrav 	}
769*b7579f77SDag-Erling Smørgrav 	x = SSL_get_peer_certificate(ssl);
770*b7579f77SDag-Erling Smørgrav 	if(!x) {
771*b7579f77SDag-Erling Smørgrav 		if(verb) printf("Server presented no peer certificate\n");
772*b7579f77SDag-Erling Smørgrav 		SSL_free(ssl);
773*b7579f77SDag-Erling Smørgrav 		return NULL;
774*b7579f77SDag-Erling Smørgrav 	}
775*b7579f77SDag-Erling Smørgrav 	verb_cert("server SSL certificate", x);
776*b7579f77SDag-Erling Smørgrav 	X509_free(x);
777*b7579f77SDag-Erling Smørgrav 	return ssl;
778*b7579f77SDag-Erling Smørgrav }
779*b7579f77SDag-Erling Smørgrav 
780*b7579f77SDag-Erling Smørgrav /** perform neat TLS shutdown */
781*b7579f77SDag-Erling Smørgrav static void
782*b7579f77SDag-Erling Smørgrav TLS_shutdown(int fd, SSL* ssl, SSL_CTX* sslctx)
783*b7579f77SDag-Erling Smørgrav {
784*b7579f77SDag-Erling Smørgrav 	/* shutdown the SSL connection nicely */
785*b7579f77SDag-Erling Smørgrav 	if(SSL_shutdown(ssl) == 0) {
786*b7579f77SDag-Erling Smørgrav 		SSL_shutdown(ssl);
787*b7579f77SDag-Erling Smørgrav 	}
788*b7579f77SDag-Erling Smørgrav 	SSL_free(ssl);
789*b7579f77SDag-Erling Smørgrav 	SSL_CTX_free(sslctx);
790*b7579f77SDag-Erling Smørgrav 	fd_close(fd);
791*b7579f77SDag-Erling Smørgrav }
792*b7579f77SDag-Erling Smørgrav 
793*b7579f77SDag-Erling Smørgrav /** write a line over SSL */
794*b7579f77SDag-Erling Smørgrav static int
795*b7579f77SDag-Erling Smørgrav write_ssl_line(SSL* ssl, char* str, char* sec)
796*b7579f77SDag-Erling Smørgrav {
797*b7579f77SDag-Erling Smørgrav 	char buf[1024];
798*b7579f77SDag-Erling Smørgrav 	size_t l;
799*b7579f77SDag-Erling Smørgrav 	if(sec) {
800*b7579f77SDag-Erling Smørgrav 		snprintf(buf, sizeof(buf), str, sec);
801*b7579f77SDag-Erling Smørgrav 	} else {
802*b7579f77SDag-Erling Smørgrav 		snprintf(buf, sizeof(buf), "%s", str);
803*b7579f77SDag-Erling Smørgrav 	}
804*b7579f77SDag-Erling Smørgrav 	l = strlen(buf);
805*b7579f77SDag-Erling Smørgrav 	if(l+2 >= sizeof(buf)) {
806*b7579f77SDag-Erling Smørgrav 		if(verb) printf("line too long\n");
807*b7579f77SDag-Erling Smørgrav 		return 0;
808*b7579f77SDag-Erling Smørgrav 	}
809*b7579f77SDag-Erling Smørgrav 	if(verb >= 2) printf("SSL_write: %s\n", buf);
810*b7579f77SDag-Erling Smørgrav 	buf[l] = '\r';
811*b7579f77SDag-Erling Smørgrav 	buf[l+1] = '\n';
812*b7579f77SDag-Erling Smørgrav 	buf[l+2] = 0;
813*b7579f77SDag-Erling Smørgrav 	/* add \r\n */
814*b7579f77SDag-Erling Smørgrav 	if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) {
815*b7579f77SDag-Erling Smørgrav 		if(verb) printf("could not SSL_write %s", str);
816*b7579f77SDag-Erling Smørgrav 		return 0;
817*b7579f77SDag-Erling Smørgrav 	}
818*b7579f77SDag-Erling Smørgrav 	return 1;
819*b7579f77SDag-Erling Smørgrav }
820*b7579f77SDag-Erling Smørgrav 
821*b7579f77SDag-Erling Smørgrav /** process header line, check rcode and keeping track of size */
822*b7579f77SDag-Erling Smørgrav static int
823*b7579f77SDag-Erling Smørgrav process_one_header(char* buf, size_t* clen, int* chunked)
824*b7579f77SDag-Erling Smørgrav {
825*b7579f77SDag-Erling Smørgrav 	if(verb>=2) printf("header: '%s'\n", buf);
826*b7579f77SDag-Erling Smørgrav 	if(strncasecmp(buf, "HTTP/1.1 ", 9) == 0) {
827*b7579f77SDag-Erling Smørgrav 		/* check returncode */
828*b7579f77SDag-Erling Smørgrav 		if(buf[9] != '2') {
829*b7579f77SDag-Erling Smørgrav 			if(verb) printf("bad status %s\n", buf+9);
830*b7579f77SDag-Erling Smørgrav 			return 0;
831*b7579f77SDag-Erling Smørgrav 		}
832*b7579f77SDag-Erling Smørgrav 	} else if(strncasecmp(buf, "Content-Length: ", 16) == 0) {
833*b7579f77SDag-Erling Smørgrav 		if(!*chunked)
834*b7579f77SDag-Erling Smørgrav 			*clen = (size_t)atoi(buf+16);
835*b7579f77SDag-Erling Smørgrav 	} else if(strncasecmp(buf, "Transfer-Encoding: chunked", 19+7) == 0) {
836*b7579f77SDag-Erling Smørgrav 		*clen = 0;
837*b7579f77SDag-Erling Smørgrav 		*chunked = 1;
838*b7579f77SDag-Erling Smørgrav 	}
839*b7579f77SDag-Erling Smørgrav 	return 1;
840*b7579f77SDag-Erling Smørgrav }
841*b7579f77SDag-Erling Smørgrav 
842*b7579f77SDag-Erling Smørgrav /**
843*b7579f77SDag-Erling Smørgrav  * Read one line from SSL
844*b7579f77SDag-Erling Smørgrav  * zero terminates.
845*b7579f77SDag-Erling Smørgrav  * skips "\r\n" (but not copied to buf).
846*b7579f77SDag-Erling Smørgrav  * @param ssl: the SSL connection to read from (blocking).
847*b7579f77SDag-Erling Smørgrav  * @param buf: buffer to return line in.
848*b7579f77SDag-Erling Smørgrav  * @param len: size of the buffer.
849*b7579f77SDag-Erling Smørgrav  * @return 0 on error, 1 on success.
850*b7579f77SDag-Erling Smørgrav  */
851*b7579f77SDag-Erling Smørgrav static int
852*b7579f77SDag-Erling Smørgrav read_ssl_line(SSL* ssl, char* buf, size_t len)
853*b7579f77SDag-Erling Smørgrav {
854*b7579f77SDag-Erling Smørgrav 	size_t n = 0;
855*b7579f77SDag-Erling Smørgrav 	int r;
856*b7579f77SDag-Erling Smørgrav 	int endnl = 0;
857*b7579f77SDag-Erling Smørgrav 	while(1) {
858*b7579f77SDag-Erling Smørgrav 		if(n >= len) {
859*b7579f77SDag-Erling Smørgrav 			if(verb) printf("line too long\n");
860*b7579f77SDag-Erling Smørgrav 			return 0;
861*b7579f77SDag-Erling Smørgrav 		}
862*b7579f77SDag-Erling Smørgrav 		if((r = SSL_read(ssl, buf+n, 1)) <= 0) {
863*b7579f77SDag-Erling Smørgrav 			if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
864*b7579f77SDag-Erling Smørgrav 				/* EOF */
865*b7579f77SDag-Erling Smørgrav 				break;
866*b7579f77SDag-Erling Smørgrav 			}
867*b7579f77SDag-Erling Smørgrav 			if(verb) printf("could not SSL_read\n");
868*b7579f77SDag-Erling Smørgrav 			return 0;
869*b7579f77SDag-Erling Smørgrav 		}
870*b7579f77SDag-Erling Smørgrav 		if(endnl && buf[n] == '\n') {
871*b7579f77SDag-Erling Smørgrav 			break;
872*b7579f77SDag-Erling Smørgrav 		} else if(endnl) {
873*b7579f77SDag-Erling Smørgrav 			/* bad data */
874*b7579f77SDag-Erling Smørgrav 			if(verb) printf("error: stray linefeeds\n");
875*b7579f77SDag-Erling Smørgrav 			return 0;
876*b7579f77SDag-Erling Smørgrav 		} else if(buf[n] == '\r') {
877*b7579f77SDag-Erling Smørgrav 			/* skip \r, and also \n on the wire */
878*b7579f77SDag-Erling Smørgrav 			endnl = 1;
879*b7579f77SDag-Erling Smørgrav 			continue;
880*b7579f77SDag-Erling Smørgrav 		} else if(buf[n] == '\n') {
881*b7579f77SDag-Erling Smørgrav 			/* skip the \n, we are done */
882*b7579f77SDag-Erling Smørgrav 			break;
883*b7579f77SDag-Erling Smørgrav 		} else n++;
884*b7579f77SDag-Erling Smørgrav 	}
885*b7579f77SDag-Erling Smørgrav 	buf[n] = 0;
886*b7579f77SDag-Erling Smørgrav 	return 1;
887*b7579f77SDag-Erling Smørgrav }
888*b7579f77SDag-Erling Smørgrav 
889*b7579f77SDag-Erling Smørgrav /** read http headers and process them */
890*b7579f77SDag-Erling Smørgrav static size_t
891*b7579f77SDag-Erling Smørgrav read_http_headers(SSL* ssl, size_t* clen)
892*b7579f77SDag-Erling Smørgrav {
893*b7579f77SDag-Erling Smørgrav 	char buf[1024];
894*b7579f77SDag-Erling Smørgrav 	int chunked = 0;
895*b7579f77SDag-Erling Smørgrav 	*clen = 0;
896*b7579f77SDag-Erling Smørgrav 	while(read_ssl_line(ssl, buf, sizeof(buf))) {
897*b7579f77SDag-Erling Smørgrav 		if(buf[0] == 0)
898*b7579f77SDag-Erling Smørgrav 			return 1;
899*b7579f77SDag-Erling Smørgrav 		if(!process_one_header(buf, clen, &chunked))
900*b7579f77SDag-Erling Smørgrav 			return 0;
901*b7579f77SDag-Erling Smørgrav 	}
902*b7579f77SDag-Erling Smørgrav 	return 0;
903*b7579f77SDag-Erling Smørgrav }
904*b7579f77SDag-Erling Smørgrav 
905*b7579f77SDag-Erling Smørgrav /** read a data chunk */
906*b7579f77SDag-Erling Smørgrav static char*
907*b7579f77SDag-Erling Smørgrav read_data_chunk(SSL* ssl, size_t len)
908*b7579f77SDag-Erling Smørgrav {
909*b7579f77SDag-Erling Smørgrav 	size_t got = 0;
910*b7579f77SDag-Erling Smørgrav 	int r;
911*b7579f77SDag-Erling Smørgrav 	char* data = malloc(len+1);
912*b7579f77SDag-Erling Smørgrav 	if(!data) {
913*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
914*b7579f77SDag-Erling Smørgrav 		return NULL;
915*b7579f77SDag-Erling Smørgrav 	}
916*b7579f77SDag-Erling Smørgrav 	while(got < len) {
917*b7579f77SDag-Erling Smørgrav 		if((r = SSL_read(ssl, data+got, (int)(len-got))) <= 0) {
918*b7579f77SDag-Erling Smørgrav 			if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
919*b7579f77SDag-Erling Smørgrav 				/* EOF */
920*b7579f77SDag-Erling Smørgrav 				if(verb) printf("could not SSL_read: unexpected EOF\n");
921*b7579f77SDag-Erling Smørgrav 				free(data);
922*b7579f77SDag-Erling Smørgrav 				return NULL;
923*b7579f77SDag-Erling Smørgrav 			}
924*b7579f77SDag-Erling Smørgrav 			if(verb) printf("could not SSL_read\n");
925*b7579f77SDag-Erling Smørgrav 			free(data);
926*b7579f77SDag-Erling Smørgrav 			return NULL;
927*b7579f77SDag-Erling Smørgrav 		}
928*b7579f77SDag-Erling Smørgrav 		if(verb >= 2) printf("at %d/%d\n", (int)got, (int)len);
929*b7579f77SDag-Erling Smørgrav 		got += r;
930*b7579f77SDag-Erling Smørgrav 	}
931*b7579f77SDag-Erling Smørgrav 	if(verb>=2) printf("read %d data\n", (int)len);
932*b7579f77SDag-Erling Smørgrav 	data[len] = 0;
933*b7579f77SDag-Erling Smørgrav 	return data;
934*b7579f77SDag-Erling Smørgrav }
935*b7579f77SDag-Erling Smørgrav 
936*b7579f77SDag-Erling Smørgrav /** parse chunk header */
937*b7579f77SDag-Erling Smørgrav static int
938*b7579f77SDag-Erling Smørgrav parse_chunk_header(char* buf, size_t* result)
939*b7579f77SDag-Erling Smørgrav {
940*b7579f77SDag-Erling Smørgrav 	char* e = NULL;
941*b7579f77SDag-Erling Smørgrav 	size_t v = (size_t)strtol(buf, &e, 16);
942*b7579f77SDag-Erling Smørgrav 	if(e == buf)
943*b7579f77SDag-Erling Smørgrav 		return 0;
944*b7579f77SDag-Erling Smørgrav 	*result = v;
945*b7579f77SDag-Erling Smørgrav 	return 1;
946*b7579f77SDag-Erling Smørgrav }
947*b7579f77SDag-Erling Smørgrav 
948*b7579f77SDag-Erling Smørgrav /** read chunked data from connection */
949*b7579f77SDag-Erling Smørgrav static BIO*
950*b7579f77SDag-Erling Smørgrav do_chunked_read(SSL* ssl)
951*b7579f77SDag-Erling Smørgrav {
952*b7579f77SDag-Erling Smørgrav 	char buf[1024];
953*b7579f77SDag-Erling Smørgrav 	size_t len;
954*b7579f77SDag-Erling Smørgrav 	char* body;
955*b7579f77SDag-Erling Smørgrav 	BIO* mem = BIO_new(BIO_s_mem());
956*b7579f77SDag-Erling Smørgrav 	if(verb>=3) printf("do_chunked_read\n");
957*b7579f77SDag-Erling Smørgrav 	if(!mem) {
958*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
959*b7579f77SDag-Erling Smørgrav 		return NULL;
960*b7579f77SDag-Erling Smørgrav 	}
961*b7579f77SDag-Erling Smørgrav 	while(read_ssl_line(ssl, buf, sizeof(buf))) {
962*b7579f77SDag-Erling Smørgrav 		/* read the chunked start line */
963*b7579f77SDag-Erling Smørgrav 		if(verb>=2) printf("chunk header: %s\n", buf);
964*b7579f77SDag-Erling Smørgrav 		if(!parse_chunk_header(buf, &len)) {
965*b7579f77SDag-Erling Smørgrav 			BIO_free(mem);
966*b7579f77SDag-Erling Smørgrav 			if(verb>=3) printf("could not parse chunk header\n");
967*b7579f77SDag-Erling Smørgrav 			return NULL;
968*b7579f77SDag-Erling Smørgrav 		}
969*b7579f77SDag-Erling Smørgrav 		if(verb>=2) printf("chunk len: %d\n", (int)len);
970*b7579f77SDag-Erling Smørgrav 		/* are we done? */
971*b7579f77SDag-Erling Smørgrav 		if(len == 0) {
972*b7579f77SDag-Erling Smørgrav 			char z = 0;
973*b7579f77SDag-Erling Smørgrav 			/* skip end-of-chunk-trailer lines,
974*b7579f77SDag-Erling Smørgrav 			 * until the empty line after that */
975*b7579f77SDag-Erling Smørgrav 			do {
976*b7579f77SDag-Erling Smørgrav 				if(!read_ssl_line(ssl, buf, sizeof(buf))) {
977*b7579f77SDag-Erling Smørgrav 					BIO_free(mem);
978*b7579f77SDag-Erling Smørgrav 					return NULL;
979*b7579f77SDag-Erling Smørgrav 				}
980*b7579f77SDag-Erling Smørgrav 			} while (strlen(buf) > 0);
981*b7579f77SDag-Erling Smørgrav 			/* end of chunks, zero terminate it */
982*b7579f77SDag-Erling Smørgrav 			if(BIO_write(mem, &z, 1) <= 0) {
983*b7579f77SDag-Erling Smørgrav 				if(verb) printf("out of memory\n");
984*b7579f77SDag-Erling Smørgrav 				BIO_free(mem);
985*b7579f77SDag-Erling Smørgrav 				return NULL;
986*b7579f77SDag-Erling Smørgrav 			}
987*b7579f77SDag-Erling Smørgrav 			return mem;
988*b7579f77SDag-Erling Smørgrav 		}
989*b7579f77SDag-Erling Smørgrav 		/* read the chunked body */
990*b7579f77SDag-Erling Smørgrav 		body = read_data_chunk(ssl, len);
991*b7579f77SDag-Erling Smørgrav 		if(!body) {
992*b7579f77SDag-Erling Smørgrav 			BIO_free(mem);
993*b7579f77SDag-Erling Smørgrav 			return NULL;
994*b7579f77SDag-Erling Smørgrav 		}
995*b7579f77SDag-Erling Smørgrav 		if(BIO_write(mem, body, (int)len) <= 0) {
996*b7579f77SDag-Erling Smørgrav 			if(verb) printf("out of memory\n");
997*b7579f77SDag-Erling Smørgrav 			free(body);
998*b7579f77SDag-Erling Smørgrav 			BIO_free(mem);
999*b7579f77SDag-Erling Smørgrav 			return NULL;
1000*b7579f77SDag-Erling Smørgrav 		}
1001*b7579f77SDag-Erling Smørgrav 		free(body);
1002*b7579f77SDag-Erling Smørgrav 		/* skip empty line after data chunk */
1003*b7579f77SDag-Erling Smørgrav 		if(!read_ssl_line(ssl, buf, sizeof(buf))) {
1004*b7579f77SDag-Erling Smørgrav 			BIO_free(mem);
1005*b7579f77SDag-Erling Smørgrav 			return NULL;
1006*b7579f77SDag-Erling Smørgrav 		}
1007*b7579f77SDag-Erling Smørgrav 	}
1008*b7579f77SDag-Erling Smørgrav 	BIO_free(mem);
1009*b7579f77SDag-Erling Smørgrav 	return NULL;
1010*b7579f77SDag-Erling Smørgrav }
1011*b7579f77SDag-Erling Smørgrav 
1012*b7579f77SDag-Erling Smørgrav /** start HTTP1.1 transaction on SSL */
1013*b7579f77SDag-Erling Smørgrav static int
1014*b7579f77SDag-Erling Smørgrav write_http_get(SSL* ssl, char* pathname, char* urlname)
1015*b7579f77SDag-Erling Smørgrav {
1016*b7579f77SDag-Erling Smørgrav 	if(write_ssl_line(ssl, "GET /%s HTTP/1.1", pathname) &&
1017*b7579f77SDag-Erling Smørgrav 	   write_ssl_line(ssl, "Host: %s", urlname) &&
1018*b7579f77SDag-Erling Smørgrav 	   write_ssl_line(ssl, "User-Agent: unbound-anchor/%s",
1019*b7579f77SDag-Erling Smørgrav 	   	PACKAGE_VERSION) &&
1020*b7579f77SDag-Erling Smørgrav 	   /* We do not really do multiple queries per connection,
1021*b7579f77SDag-Erling Smørgrav 	    * but this header setting is also not needed.
1022*b7579f77SDag-Erling Smørgrav 	    * write_ssl_line(ssl, "Connection: close", NULL) &&*/
1023*b7579f77SDag-Erling Smørgrav 	   write_ssl_line(ssl, "", NULL)) {
1024*b7579f77SDag-Erling Smørgrav 		return 1;
1025*b7579f77SDag-Erling Smørgrav 	}
1026*b7579f77SDag-Erling Smørgrav 	return 0;
1027*b7579f77SDag-Erling Smørgrav }
1028*b7579f77SDag-Erling Smørgrav 
1029*b7579f77SDag-Erling Smørgrav /** read chunked data and zero terminate; len is without zero */
1030*b7579f77SDag-Erling Smørgrav static char*
1031*b7579f77SDag-Erling Smørgrav read_chunked_zero_terminate(SSL* ssl, size_t* len)
1032*b7579f77SDag-Erling Smørgrav {
1033*b7579f77SDag-Erling Smørgrav 	/* do the chunked version */
1034*b7579f77SDag-Erling Smørgrav 	BIO* tmp = do_chunked_read(ssl);
1035*b7579f77SDag-Erling Smørgrav 	char* data, *d = NULL;
1036*b7579f77SDag-Erling Smørgrav 	size_t l;
1037*b7579f77SDag-Erling Smørgrav 	if(!tmp) {
1038*b7579f77SDag-Erling Smørgrav 		if(verb) printf("could not read from https\n");
1039*b7579f77SDag-Erling Smørgrav 		return NULL;
1040*b7579f77SDag-Erling Smørgrav 	}
1041*b7579f77SDag-Erling Smørgrav 	l = (size_t)BIO_get_mem_data(tmp, &d);
1042*b7579f77SDag-Erling Smørgrav 	if(verb>=2) printf("chunked data is %d\n", (int)l);
1043*b7579f77SDag-Erling Smørgrav 	if(l == 0 || d == NULL) {
1044*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1045*b7579f77SDag-Erling Smørgrav 		return NULL;
1046*b7579f77SDag-Erling Smørgrav 	}
1047*b7579f77SDag-Erling Smørgrav 	*len = l-1;
1048*b7579f77SDag-Erling Smørgrav 	data = (char*)malloc(l);
1049*b7579f77SDag-Erling Smørgrav 	if(data == NULL) {
1050*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1051*b7579f77SDag-Erling Smørgrav 		return NULL;
1052*b7579f77SDag-Erling Smørgrav 	}
1053*b7579f77SDag-Erling Smørgrav 	memcpy(data, d, l);
1054*b7579f77SDag-Erling Smørgrav 	BIO_free(tmp);
1055*b7579f77SDag-Erling Smørgrav 	return data;
1056*b7579f77SDag-Erling Smørgrav }
1057*b7579f77SDag-Erling Smørgrav 
1058*b7579f77SDag-Erling Smørgrav /** read HTTP result from SSL */
1059*b7579f77SDag-Erling Smørgrav static BIO*
1060*b7579f77SDag-Erling Smørgrav read_http_result(SSL* ssl)
1061*b7579f77SDag-Erling Smørgrav {
1062*b7579f77SDag-Erling Smørgrav 	size_t len = 0;
1063*b7579f77SDag-Erling Smørgrav 	char* data;
1064*b7579f77SDag-Erling Smørgrav 	BIO* m;
1065*b7579f77SDag-Erling Smørgrav 	if(!read_http_headers(ssl, &len)) {
1066*b7579f77SDag-Erling Smørgrav 		return NULL;
1067*b7579f77SDag-Erling Smørgrav 	}
1068*b7579f77SDag-Erling Smørgrav 	if(len == 0) {
1069*b7579f77SDag-Erling Smørgrav 		data = read_chunked_zero_terminate(ssl, &len);
1070*b7579f77SDag-Erling Smørgrav 	} else {
1071*b7579f77SDag-Erling Smørgrav 		data = read_data_chunk(ssl, len);
1072*b7579f77SDag-Erling Smørgrav 	}
1073*b7579f77SDag-Erling Smørgrav 	if(!data) return NULL;
1074*b7579f77SDag-Erling Smørgrav 	if(verb >= 4) print_data("read data", data, (int)len);
1075*b7579f77SDag-Erling Smørgrav 	m = BIO_new_mem_buf(data, (int)len);
1076*b7579f77SDag-Erling Smørgrav 	if(!m) {
1077*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1078*b7579f77SDag-Erling Smørgrav 		exit(0);
1079*b7579f77SDag-Erling Smørgrav 	}
1080*b7579f77SDag-Erling Smørgrav 	return m;
1081*b7579f77SDag-Erling Smørgrav }
1082*b7579f77SDag-Erling Smørgrav 
1083*b7579f77SDag-Erling Smørgrav /** https to an IP addr, return BIO with pathname or NULL */
1084*b7579f77SDag-Erling Smørgrav static BIO*
1085*b7579f77SDag-Erling Smørgrav https_to_ip(struct ip_list* ip, char* pathname, char* urlname)
1086*b7579f77SDag-Erling Smørgrav {
1087*b7579f77SDag-Erling Smørgrav 	int fd;
1088*b7579f77SDag-Erling Smørgrav 	SSL* ssl;
1089*b7579f77SDag-Erling Smørgrav 	BIO* bio;
1090*b7579f77SDag-Erling Smørgrav 	SSL_CTX* sslctx = setup_sslctx();
1091*b7579f77SDag-Erling Smørgrav 	if(!sslctx) {
1092*b7579f77SDag-Erling Smørgrav 		return NULL;
1093*b7579f77SDag-Erling Smørgrav 	}
1094*b7579f77SDag-Erling Smørgrav 	fd = connect_to_ip(ip);
1095*b7579f77SDag-Erling Smørgrav 	if(fd == -1) {
1096*b7579f77SDag-Erling Smørgrav 		SSL_CTX_free(sslctx);
1097*b7579f77SDag-Erling Smørgrav 		return NULL;
1098*b7579f77SDag-Erling Smørgrav 	}
1099*b7579f77SDag-Erling Smørgrav 	ssl = TLS_initiate(sslctx, fd);
1100*b7579f77SDag-Erling Smørgrav 	if(!ssl) {
1101*b7579f77SDag-Erling Smørgrav 		SSL_CTX_free(sslctx);
1102*b7579f77SDag-Erling Smørgrav 		fd_close(fd);
1103*b7579f77SDag-Erling Smørgrav 		return NULL;
1104*b7579f77SDag-Erling Smørgrav 	}
1105*b7579f77SDag-Erling Smørgrav 	if(!write_http_get(ssl, pathname, urlname)) {
1106*b7579f77SDag-Erling Smørgrav 		if(verb) printf("could not write to server\n");
1107*b7579f77SDag-Erling Smørgrav 		SSL_free(ssl);
1108*b7579f77SDag-Erling Smørgrav 		SSL_CTX_free(sslctx);
1109*b7579f77SDag-Erling Smørgrav 		fd_close(fd);
1110*b7579f77SDag-Erling Smørgrav 		return NULL;
1111*b7579f77SDag-Erling Smørgrav 	}
1112*b7579f77SDag-Erling Smørgrav 	bio = read_http_result(ssl);
1113*b7579f77SDag-Erling Smørgrav 	TLS_shutdown(fd, ssl, sslctx);
1114*b7579f77SDag-Erling Smørgrav 	return bio;
1115*b7579f77SDag-Erling Smørgrav }
1116*b7579f77SDag-Erling Smørgrav 
1117*b7579f77SDag-Erling Smørgrav /**
1118*b7579f77SDag-Erling Smørgrav  * Do a HTTPS, HTTP1.1 over TLS, to fetch a file
1119*b7579f77SDag-Erling Smørgrav  * @param ip_list: list of IP addresses to use to fetch from.
1120*b7579f77SDag-Erling Smørgrav  * @param pathname: pathname of file on server to GET.
1121*b7579f77SDag-Erling Smørgrav  * @param urlname: name to pass as the virtual host for this request.
1122*b7579f77SDag-Erling Smørgrav  * @return a memory BIO with the file in it.
1123*b7579f77SDag-Erling Smørgrav  */
1124*b7579f77SDag-Erling Smørgrav static BIO*
1125*b7579f77SDag-Erling Smørgrav https(struct ip_list* ip_list, char* pathname, char* urlname)
1126*b7579f77SDag-Erling Smørgrav {
1127*b7579f77SDag-Erling Smørgrav 	struct ip_list* ip;
1128*b7579f77SDag-Erling Smørgrav 	BIO* bio = NULL;
1129*b7579f77SDag-Erling Smørgrav 	/* try random address first, and work through the list */
1130*b7579f77SDag-Erling Smørgrav 	wipe_ip_usage(ip_list);
1131*b7579f77SDag-Erling Smørgrav 	while( (ip = pick_random_ip(ip_list)) ) {
1132*b7579f77SDag-Erling Smørgrav 		ip->used = 1;
1133*b7579f77SDag-Erling Smørgrav 		bio = https_to_ip(ip, pathname, urlname);
1134*b7579f77SDag-Erling Smørgrav 		if(bio) break;
1135*b7579f77SDag-Erling Smørgrav 	}
1136*b7579f77SDag-Erling Smørgrav 	if(!bio) {
1137*b7579f77SDag-Erling Smørgrav 		if(verb) printf("could not fetch %s\n", pathname);
1138*b7579f77SDag-Erling Smørgrav 		exit(0);
1139*b7579f77SDag-Erling Smørgrav 	} else {
1140*b7579f77SDag-Erling Smørgrav 		if(verb) printf("fetched %s (%d bytes)\n",
1141*b7579f77SDag-Erling Smørgrav 			pathname, (int)BIO_ctrl_pending(bio));
1142*b7579f77SDag-Erling Smørgrav 	}
1143*b7579f77SDag-Erling Smørgrav 	return bio;
1144*b7579f77SDag-Erling Smørgrav }
1145*b7579f77SDag-Erling Smørgrav 
1146*b7579f77SDag-Erling Smørgrav /** free up a downloaded file BIO */
1147*b7579f77SDag-Erling Smørgrav static void
1148*b7579f77SDag-Erling Smørgrav free_file_bio(BIO* bio)
1149*b7579f77SDag-Erling Smørgrav {
1150*b7579f77SDag-Erling Smørgrav 	char* pp = NULL;
1151*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(bio);
1152*b7579f77SDag-Erling Smørgrav 	(void)BIO_get_mem_data(bio, &pp);
1153*b7579f77SDag-Erling Smørgrav 	free(pp);
1154*b7579f77SDag-Erling Smørgrav 	BIO_free(bio);
1155*b7579f77SDag-Erling Smørgrav }
1156*b7579f77SDag-Erling Smørgrav 
1157*b7579f77SDag-Erling Smørgrav /** XML parse private data during the parse */
1158*b7579f77SDag-Erling Smørgrav struct xml_data {
1159*b7579f77SDag-Erling Smørgrav 	/** the parser, reference */
1160*b7579f77SDag-Erling Smørgrav 	XML_Parser parser;
1161*b7579f77SDag-Erling Smørgrav 	/** the current tag; malloced; or NULL outside of tags */
1162*b7579f77SDag-Erling Smørgrav 	char* tag;
1163*b7579f77SDag-Erling Smørgrav 	/** current date to use during the parse */
1164*b7579f77SDag-Erling Smørgrav 	time_t date;
1165*b7579f77SDag-Erling Smørgrav 	/** number of keys usefully read in */
1166*b7579f77SDag-Erling Smørgrav 	int num_keys;
1167*b7579f77SDag-Erling Smørgrav 	/** the compiled anchors as DS records */
1168*b7579f77SDag-Erling Smørgrav 	BIO* ds;
1169*b7579f77SDag-Erling Smørgrav 
1170*b7579f77SDag-Erling Smørgrav 	/** do we want to use this anchor? */
1171*b7579f77SDag-Erling Smørgrav 	int use_key;
1172*b7579f77SDag-Erling Smørgrav 	/** the current anchor: Zone */
1173*b7579f77SDag-Erling Smørgrav 	BIO* czone;
1174*b7579f77SDag-Erling Smørgrav 	/** the current anchor: KeyTag */
1175*b7579f77SDag-Erling Smørgrav 	BIO* ctag;
1176*b7579f77SDag-Erling Smørgrav 	/** the current anchor: Algorithm */
1177*b7579f77SDag-Erling Smørgrav 	BIO* calgo;
1178*b7579f77SDag-Erling Smørgrav 	/** the current anchor: DigestType */
1179*b7579f77SDag-Erling Smørgrav 	BIO* cdigtype;
1180*b7579f77SDag-Erling Smørgrav 	/** the current anchor: Digest*/
1181*b7579f77SDag-Erling Smørgrav 	BIO* cdigest;
1182*b7579f77SDag-Erling Smørgrav };
1183*b7579f77SDag-Erling Smørgrav 
1184*b7579f77SDag-Erling Smørgrav /** The BIO for the tag */
1185*b7579f77SDag-Erling Smørgrav static BIO*
1186*b7579f77SDag-Erling Smørgrav xml_selectbio(struct xml_data* data, const char* tag)
1187*b7579f77SDag-Erling Smørgrav {
1188*b7579f77SDag-Erling Smørgrav 	BIO* b = NULL;
1189*b7579f77SDag-Erling Smørgrav 	if(strcasecmp(tag, "KeyTag") == 0)
1190*b7579f77SDag-Erling Smørgrav 		b = data->ctag;
1191*b7579f77SDag-Erling Smørgrav 	else if(strcasecmp(tag, "Algorithm") == 0)
1192*b7579f77SDag-Erling Smørgrav 		b = data->calgo;
1193*b7579f77SDag-Erling Smørgrav 	else if(strcasecmp(tag, "DigestType") == 0)
1194*b7579f77SDag-Erling Smørgrav 		b = data->cdigtype;
1195*b7579f77SDag-Erling Smørgrav 	else if(strcasecmp(tag, "Digest") == 0)
1196*b7579f77SDag-Erling Smørgrav 		b = data->cdigest;
1197*b7579f77SDag-Erling Smørgrav 	return b;
1198*b7579f77SDag-Erling Smørgrav }
1199*b7579f77SDag-Erling Smørgrav 
1200*b7579f77SDag-Erling Smørgrav /**
1201*b7579f77SDag-Erling Smørgrav  * XML handle character data, the data inside an element.
1202*b7579f77SDag-Erling Smørgrav  * @param userData: xml_data structure
1203*b7579f77SDag-Erling Smørgrav  * @param s: the character data.  May not all be in one callback.
1204*b7579f77SDag-Erling Smørgrav  * 	NOT zero terminated.
1205*b7579f77SDag-Erling Smørgrav  * @param len: length of this part of the data.
1206*b7579f77SDag-Erling Smørgrav  */
1207*b7579f77SDag-Erling Smørgrav void
1208*b7579f77SDag-Erling Smørgrav xml_charhandle(void *userData, const XML_Char *s, int len)
1209*b7579f77SDag-Erling Smørgrav {
1210*b7579f77SDag-Erling Smørgrav 	struct xml_data* data = (struct xml_data*)userData;
1211*b7579f77SDag-Erling Smørgrav 	BIO* b = NULL;
1212*b7579f77SDag-Erling Smørgrav 	/* skip characters outside of elements */
1213*b7579f77SDag-Erling Smørgrav 	if(!data->tag)
1214*b7579f77SDag-Erling Smørgrav 		return;
1215*b7579f77SDag-Erling Smørgrav 	if(verb>=4) {
1216*b7579f77SDag-Erling Smørgrav 		int i;
1217*b7579f77SDag-Erling Smørgrav 		printf("%s%s charhandle: '",
1218*b7579f77SDag-Erling Smørgrav 			data->use_key?"use ":"",
1219*b7579f77SDag-Erling Smørgrav 			data->tag?data->tag:"none");
1220*b7579f77SDag-Erling Smørgrav 		for(i=0; i<len; i++)
1221*b7579f77SDag-Erling Smørgrav 			printf("%c", s[i]);
1222*b7579f77SDag-Erling Smørgrav 		printf("'\n");
1223*b7579f77SDag-Erling Smørgrav 	}
1224*b7579f77SDag-Erling Smørgrav 	if(strcasecmp(data->tag, "Zone") == 0) {
1225*b7579f77SDag-Erling Smørgrav 		if(BIO_write(data->czone, s, len) <= 0) {
1226*b7579f77SDag-Erling Smørgrav 			if(verb) printf("out of memory in BIO_write\n");
1227*b7579f77SDag-Erling Smørgrav 			exit(0);
1228*b7579f77SDag-Erling Smørgrav 		}
1229*b7579f77SDag-Erling Smørgrav 		return;
1230*b7579f77SDag-Erling Smørgrav 	}
1231*b7579f77SDag-Erling Smørgrav 	/* only store if key is used */
1232*b7579f77SDag-Erling Smørgrav 	if(!data->use_key)
1233*b7579f77SDag-Erling Smørgrav 		return;
1234*b7579f77SDag-Erling Smørgrav 	b = xml_selectbio(data, data->tag);
1235*b7579f77SDag-Erling Smørgrav 	if(b) {
1236*b7579f77SDag-Erling Smørgrav 		if(BIO_write(b, s, len) <= 0) {
1237*b7579f77SDag-Erling Smørgrav 			if(verb) printf("out of memory in BIO_write\n");
1238*b7579f77SDag-Erling Smørgrav 			exit(0);
1239*b7579f77SDag-Erling Smørgrav 		}
1240*b7579f77SDag-Erling Smørgrav 	}
1241*b7579f77SDag-Erling Smørgrav }
1242*b7579f77SDag-Erling Smørgrav 
1243*b7579f77SDag-Erling Smørgrav /**
1244*b7579f77SDag-Erling Smørgrav  * XML fetch value of particular attribute(by name) or NULL if not present.
1245*b7579f77SDag-Erling Smørgrav  * @param atts: attribute array (from xml_startelem).
1246*b7579f77SDag-Erling Smørgrav  * @param name: name of attribute to look for.
1247*b7579f77SDag-Erling Smørgrav  * @return the value or NULL. (ptr into atts).
1248*b7579f77SDag-Erling Smørgrav  */
1249*b7579f77SDag-Erling Smørgrav static const XML_Char*
1250*b7579f77SDag-Erling Smørgrav find_att(const XML_Char **atts, XML_Char* name)
1251*b7579f77SDag-Erling Smørgrav {
1252*b7579f77SDag-Erling Smørgrav 	int i;
1253*b7579f77SDag-Erling Smørgrav 	for(i=0; atts[i]; i+=2) {
1254*b7579f77SDag-Erling Smørgrav 		if(strcasecmp(atts[i], name) == 0)
1255*b7579f77SDag-Erling Smørgrav 			return atts[i+1];
1256*b7579f77SDag-Erling Smørgrav 	}
1257*b7579f77SDag-Erling Smørgrav 	return NULL;
1258*b7579f77SDag-Erling Smørgrav }
1259*b7579f77SDag-Erling Smørgrav 
1260*b7579f77SDag-Erling Smørgrav /**
1261*b7579f77SDag-Erling Smørgrav  * XML convert DateTime element to time_t.
1262*b7579f77SDag-Erling Smørgrav  * [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
1263*b7579f77SDag-Erling Smørgrav  * (with optional .ssssss fractional seconds)
1264*b7579f77SDag-Erling Smørgrav  * @param str: the string
1265*b7579f77SDag-Erling Smørgrav  * @return a time_t representation or 0 on failure.
1266*b7579f77SDag-Erling Smørgrav  */
1267*b7579f77SDag-Erling Smørgrav static time_t
1268*b7579f77SDag-Erling Smørgrav xml_convertdate(const char* str)
1269*b7579f77SDag-Erling Smørgrav {
1270*b7579f77SDag-Erling Smørgrav 	time_t t = 0;
1271*b7579f77SDag-Erling Smørgrav 	struct tm tm;
1272*b7579f77SDag-Erling Smørgrav 	const char* s;
1273*b7579f77SDag-Erling Smørgrav 	/* for this application, ignore minus in front;
1274*b7579f77SDag-Erling Smørgrav 	 * only positive dates are expected */
1275*b7579f77SDag-Erling Smørgrav 	s = str;
1276*b7579f77SDag-Erling Smørgrav 	if(s[0] == '-') s++;
1277*b7579f77SDag-Erling Smørgrav 	memset(&tm, 0, sizeof(tm));
1278*b7579f77SDag-Erling Smørgrav 	/* parse initial content of the string (lots of whitespace allowed) */
1279*b7579f77SDag-Erling Smørgrav 	s = strptime(s, "%t%Y%t-%t%m%t-%t%d%tT%t%H%t:%t%M%t:%t%S%t", &tm);
1280*b7579f77SDag-Erling Smørgrav 	if(!s) {
1281*b7579f77SDag-Erling Smørgrav 		if(verb) printf("xml_convertdate parse failure %s\n", str);
1282*b7579f77SDag-Erling Smørgrav 		return 0;
1283*b7579f77SDag-Erling Smørgrav 	}
1284*b7579f77SDag-Erling Smørgrav 	/* parse remainder of date string */
1285*b7579f77SDag-Erling Smørgrav 	if(*s == '.') {
1286*b7579f77SDag-Erling Smørgrav 		/* optional '.' and fractional seconds */
1287*b7579f77SDag-Erling Smørgrav 		int frac = 0, n = 0;
1288*b7579f77SDag-Erling Smørgrav 		if(sscanf(s+1, "%d%n", &frac, &n) < 1) {
1289*b7579f77SDag-Erling Smørgrav 			if(verb) printf("xml_convertdate f failure %s\n", str);
1290*b7579f77SDag-Erling Smørgrav 			return 0;
1291*b7579f77SDag-Erling Smørgrav 		}
1292*b7579f77SDag-Erling Smørgrav 		/* fraction is not used, time_t has second accuracy */
1293*b7579f77SDag-Erling Smørgrav 		s++;
1294*b7579f77SDag-Erling Smørgrav 		s+=n;
1295*b7579f77SDag-Erling Smørgrav 	}
1296*b7579f77SDag-Erling Smørgrav 	if(*s == 'Z' || *s == 'z') {
1297*b7579f77SDag-Erling Smørgrav 		/* nothing to do for this */
1298*b7579f77SDag-Erling Smørgrav 		s++;
1299*b7579f77SDag-Erling Smørgrav 	} else if(*s == '+' || *s == '-') {
1300*b7579f77SDag-Erling Smørgrav 		/* optional timezone spec: Z or +hh:mm or -hh:mm */
1301*b7579f77SDag-Erling Smørgrav 		int hr = 0, mn = 0, n = 0;
1302*b7579f77SDag-Erling Smørgrav 		if(sscanf(s+1, "%d:%d%n", &hr, &mn, &n) < 2) {
1303*b7579f77SDag-Erling Smørgrav 			if(verb) printf("xml_convertdate tz failure %s\n", str);
1304*b7579f77SDag-Erling Smørgrav 			return 0;
1305*b7579f77SDag-Erling Smørgrav 		}
1306*b7579f77SDag-Erling Smørgrav 		if(*s == '+') {
1307*b7579f77SDag-Erling Smørgrav 			tm.tm_hour += hr;
1308*b7579f77SDag-Erling Smørgrav 			tm.tm_min += mn;
1309*b7579f77SDag-Erling Smørgrav 		} else {
1310*b7579f77SDag-Erling Smørgrav 			tm.tm_hour -= hr;
1311*b7579f77SDag-Erling Smørgrav 			tm.tm_min -= mn;
1312*b7579f77SDag-Erling Smørgrav 		}
1313*b7579f77SDag-Erling Smørgrav 		s++;
1314*b7579f77SDag-Erling Smørgrav 		s += n;
1315*b7579f77SDag-Erling Smørgrav 	}
1316*b7579f77SDag-Erling Smørgrav 	if(*s != 0) {
1317*b7579f77SDag-Erling Smørgrav 		/* not ended properly */
1318*b7579f77SDag-Erling Smørgrav 		/* but ignore, (lenient) */
1319*b7579f77SDag-Erling Smørgrav 	}
1320*b7579f77SDag-Erling Smørgrav 
1321*b7579f77SDag-Erling Smørgrav 	t = mktime(&tm);
1322*b7579f77SDag-Erling Smørgrav 	if(t == (time_t)-1) {
1323*b7579f77SDag-Erling Smørgrav 		if(verb) printf("xml_convertdate mktime failure\n");
1324*b7579f77SDag-Erling Smørgrav 		return 0;
1325*b7579f77SDag-Erling Smørgrav 	}
1326*b7579f77SDag-Erling Smørgrav 	return t;
1327*b7579f77SDag-Erling Smørgrav }
1328*b7579f77SDag-Erling Smørgrav 
1329*b7579f77SDag-Erling Smørgrav /**
1330*b7579f77SDag-Erling Smørgrav  * XML handle the KeyDigest start tag, check validity periods.
1331*b7579f77SDag-Erling Smørgrav  */
1332*b7579f77SDag-Erling Smørgrav static void
1333*b7579f77SDag-Erling Smørgrav handle_keydigest(struct xml_data* data, const XML_Char **atts)
1334*b7579f77SDag-Erling Smørgrav {
1335*b7579f77SDag-Erling Smørgrav 	data->use_key = 0;
1336*b7579f77SDag-Erling Smørgrav 	if(find_att(atts, "validFrom")) {
1337*b7579f77SDag-Erling Smørgrav 		time_t from = xml_convertdate(find_att(atts, "validFrom"));
1338*b7579f77SDag-Erling Smørgrav 		if(from == 0) {
1339*b7579f77SDag-Erling Smørgrav 			if(verb) printf("error: xml cannot be parsed\n");
1340*b7579f77SDag-Erling Smørgrav 			exit(0);
1341*b7579f77SDag-Erling Smørgrav 		}
1342*b7579f77SDag-Erling Smørgrav 		if(data->date < from)
1343*b7579f77SDag-Erling Smørgrav 			return;
1344*b7579f77SDag-Erling Smørgrav 	}
1345*b7579f77SDag-Erling Smørgrav 	if(find_att(atts, "validUntil")) {
1346*b7579f77SDag-Erling Smørgrav 		time_t until = xml_convertdate(find_att(atts, "validUntil"));
1347*b7579f77SDag-Erling Smørgrav 		if(until == 0) {
1348*b7579f77SDag-Erling Smørgrav 			if(verb) printf("error: xml cannot be parsed\n");
1349*b7579f77SDag-Erling Smørgrav 			exit(0);
1350*b7579f77SDag-Erling Smørgrav 		}
1351*b7579f77SDag-Erling Smørgrav 		if(data->date > until)
1352*b7579f77SDag-Erling Smørgrav 			return;
1353*b7579f77SDag-Erling Smørgrav 	}
1354*b7579f77SDag-Erling Smørgrav 	/* yes we want to use this key */
1355*b7579f77SDag-Erling Smørgrav 	data->use_key = 1;
1356*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(data->ctag);
1357*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(data->calgo);
1358*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(data->cdigtype);
1359*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(data->cdigest);
1360*b7579f77SDag-Erling Smørgrav }
1361*b7579f77SDag-Erling Smørgrav 
1362*b7579f77SDag-Erling Smørgrav /** See if XML element equals the zone name */
1363*b7579f77SDag-Erling Smørgrav static int
1364*b7579f77SDag-Erling Smørgrav xml_is_zone_name(BIO* zone, char* name)
1365*b7579f77SDag-Erling Smørgrav {
1366*b7579f77SDag-Erling Smørgrav 	char buf[1024];
1367*b7579f77SDag-Erling Smørgrav 	char* z = NULL;
1368*b7579f77SDag-Erling Smørgrav 	long zlen;
1369*b7579f77SDag-Erling Smørgrav 	(void)BIO_seek(zone, 0);
1370*b7579f77SDag-Erling Smørgrav 	zlen = BIO_get_mem_data(zone, &z);
1371*b7579f77SDag-Erling Smørgrav 	if(!zlen || !z) return 0;
1372*b7579f77SDag-Erling Smørgrav 	/* zero terminate */
1373*b7579f77SDag-Erling Smørgrav 	if(zlen >= (long)sizeof(buf)) return 0;
1374*b7579f77SDag-Erling Smørgrav 	memmove(buf, z, (size_t)zlen);
1375*b7579f77SDag-Erling Smørgrav 	buf[zlen] = 0;
1376*b7579f77SDag-Erling Smørgrav 	/* compare */
1377*b7579f77SDag-Erling Smørgrav 	return (strncasecmp(buf, name, strlen(name)) == 0);
1378*b7579f77SDag-Erling Smørgrav }
1379*b7579f77SDag-Erling Smørgrav 
1380*b7579f77SDag-Erling Smørgrav /**
1381*b7579f77SDag-Erling Smørgrav  * XML start of element. This callback is called whenever an XML tag starts.
1382*b7579f77SDag-Erling Smørgrav  * XML_Char is UTF8.
1383*b7579f77SDag-Erling Smørgrav  * @param userData: the xml_data structure.
1384*b7579f77SDag-Erling Smørgrav  * @param name: the tag that starts.
1385*b7579f77SDag-Erling Smørgrav  * @param atts: array of strings, pairs of attr = value, ends with NULL.
1386*b7579f77SDag-Erling Smørgrav  * 	i.e. att[0]="att[1]" att[2]="att[3]" att[4]isNull
1387*b7579f77SDag-Erling Smørgrav  */
1388*b7579f77SDag-Erling Smørgrav static void
1389*b7579f77SDag-Erling Smørgrav xml_startelem(void *userData, const XML_Char *name, const XML_Char **atts)
1390*b7579f77SDag-Erling Smørgrav {
1391*b7579f77SDag-Erling Smørgrav 	struct xml_data* data = (struct xml_data*)userData;
1392*b7579f77SDag-Erling Smørgrav 	BIO* b;
1393*b7579f77SDag-Erling Smørgrav 	if(verb>=4) printf("xml tag start '%s'\n", name);
1394*b7579f77SDag-Erling Smørgrav 	free(data->tag);
1395*b7579f77SDag-Erling Smørgrav 	data->tag = strdup(name);
1396*b7579f77SDag-Erling Smørgrav 	if(!data->tag) {
1397*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1398*b7579f77SDag-Erling Smørgrav 		exit(0);
1399*b7579f77SDag-Erling Smørgrav 	}
1400*b7579f77SDag-Erling Smørgrav 	if(verb>=4) {
1401*b7579f77SDag-Erling Smørgrav 		int i;
1402*b7579f77SDag-Erling Smørgrav 		for(i=0; atts[i]; i+=2) {
1403*b7579f77SDag-Erling Smørgrav 			printf("  %s='%s'\n", atts[i], atts[i+1]);
1404*b7579f77SDag-Erling Smørgrav 		}
1405*b7579f77SDag-Erling Smørgrav 	}
1406*b7579f77SDag-Erling Smørgrav 	/* handle attributes to particular types */
1407*b7579f77SDag-Erling Smørgrav 	if(strcasecmp(name, "KeyDigest") == 0) {
1408*b7579f77SDag-Erling Smørgrav 		handle_keydigest(data, atts);
1409*b7579f77SDag-Erling Smørgrav 		return;
1410*b7579f77SDag-Erling Smørgrav 	} else if(strcasecmp(name, "Zone") == 0) {
1411*b7579f77SDag-Erling Smørgrav 		(void)BIO_reset(data->czone);
1412*b7579f77SDag-Erling Smørgrav 		return;
1413*b7579f77SDag-Erling Smørgrav 	}
1414*b7579f77SDag-Erling Smørgrav 
1415*b7579f77SDag-Erling Smørgrav 	/* for other types we prepare to pick up the data */
1416*b7579f77SDag-Erling Smørgrav 	if(!data->use_key)
1417*b7579f77SDag-Erling Smørgrav 		return;
1418*b7579f77SDag-Erling Smørgrav 	b = xml_selectbio(data, data->tag);
1419*b7579f77SDag-Erling Smørgrav 	if(b) {
1420*b7579f77SDag-Erling Smørgrav 		/* empty it */
1421*b7579f77SDag-Erling Smørgrav 		(void)BIO_reset(b);
1422*b7579f77SDag-Erling Smørgrav 	}
1423*b7579f77SDag-Erling Smørgrav }
1424*b7579f77SDag-Erling Smørgrav 
1425*b7579f77SDag-Erling Smørgrav /** Append str to bio */
1426*b7579f77SDag-Erling Smørgrav static void
1427*b7579f77SDag-Erling Smørgrav xml_append_str(BIO* b, const char* s)
1428*b7579f77SDag-Erling Smørgrav {
1429*b7579f77SDag-Erling Smørgrav 	if(BIO_write(b, s, (int)strlen(s)) <= 0) {
1430*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory in BIO_write\n");
1431*b7579f77SDag-Erling Smørgrav 		exit(0);
1432*b7579f77SDag-Erling Smørgrav 	}
1433*b7579f77SDag-Erling Smørgrav }
1434*b7579f77SDag-Erling Smørgrav 
1435*b7579f77SDag-Erling Smørgrav /** Append bio to bio */
1436*b7579f77SDag-Erling Smørgrav static void
1437*b7579f77SDag-Erling Smørgrav xml_append_bio(BIO* b, BIO* a)
1438*b7579f77SDag-Erling Smørgrav {
1439*b7579f77SDag-Erling Smørgrav 	char* z = NULL;
1440*b7579f77SDag-Erling Smørgrav 	long i, len;
1441*b7579f77SDag-Erling Smørgrav 	(void)BIO_seek(a, 0);
1442*b7579f77SDag-Erling Smørgrav 	len = BIO_get_mem_data(a, &z);
1443*b7579f77SDag-Erling Smørgrav 	if(!len || !z) {
1444*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory in BIO_write\n");
1445*b7579f77SDag-Erling Smørgrav 		exit(0);
1446*b7579f77SDag-Erling Smørgrav 	}
1447*b7579f77SDag-Erling Smørgrav 	/* remove newlines in the data here */
1448*b7579f77SDag-Erling Smørgrav 	for(i=0; i<len; i++) {
1449*b7579f77SDag-Erling Smørgrav 		if(z[i] == '\r' || z[i] == '\n')
1450*b7579f77SDag-Erling Smørgrav 			z[i] = ' ';
1451*b7579f77SDag-Erling Smørgrav 	}
1452*b7579f77SDag-Erling Smørgrav 	/* write to BIO */
1453*b7579f77SDag-Erling Smørgrav 	if(BIO_write(b, z, len) <= 0) {
1454*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory in BIO_write\n");
1455*b7579f77SDag-Erling Smørgrav 		exit(0);
1456*b7579f77SDag-Erling Smørgrav 	}
1457*b7579f77SDag-Erling Smørgrav }
1458*b7579f77SDag-Erling Smørgrav 
1459*b7579f77SDag-Erling Smørgrav /** write the parsed xml-DS to the DS list */
1460*b7579f77SDag-Erling Smørgrav static void
1461*b7579f77SDag-Erling Smørgrav xml_append_ds(struct xml_data* data)
1462*b7579f77SDag-Erling Smørgrav {
1463*b7579f77SDag-Erling Smørgrav 	/* write DS to accumulated DS */
1464*b7579f77SDag-Erling Smørgrav 	xml_append_str(data->ds, ". IN DS ");
1465*b7579f77SDag-Erling Smørgrav 	xml_append_bio(data->ds, data->ctag);
1466*b7579f77SDag-Erling Smørgrav 	xml_append_str(data->ds, " ");
1467*b7579f77SDag-Erling Smørgrav 	xml_append_bio(data->ds, data->calgo);
1468*b7579f77SDag-Erling Smørgrav 	xml_append_str(data->ds, " ");
1469*b7579f77SDag-Erling Smørgrav 	xml_append_bio(data->ds, data->cdigtype);
1470*b7579f77SDag-Erling Smørgrav 	xml_append_str(data->ds, " ");
1471*b7579f77SDag-Erling Smørgrav 	xml_append_bio(data->ds, data->cdigest);
1472*b7579f77SDag-Erling Smørgrav 	xml_append_str(data->ds, "\n");
1473*b7579f77SDag-Erling Smørgrav 	data->num_keys++;
1474*b7579f77SDag-Erling Smørgrav }
1475*b7579f77SDag-Erling Smørgrav 
1476*b7579f77SDag-Erling Smørgrav /**
1477*b7579f77SDag-Erling Smørgrav  * XML end of element. This callback is called whenever an XML tag ends.
1478*b7579f77SDag-Erling Smørgrav  * XML_Char is UTF8.
1479*b7579f77SDag-Erling Smørgrav  * @param userData: the xml_data structure
1480*b7579f77SDag-Erling Smørgrav  * @param name: the tag that ends.
1481*b7579f77SDag-Erling Smørgrav  */
1482*b7579f77SDag-Erling Smørgrav static void
1483*b7579f77SDag-Erling Smørgrav xml_endelem(void *userData, const XML_Char *name)
1484*b7579f77SDag-Erling Smørgrav {
1485*b7579f77SDag-Erling Smørgrav 	struct xml_data* data = (struct xml_data*)userData;
1486*b7579f77SDag-Erling Smørgrav 	if(verb>=4) printf("xml tag end   '%s'\n", name);
1487*b7579f77SDag-Erling Smørgrav 	free(data->tag);
1488*b7579f77SDag-Erling Smørgrav 	data->tag = NULL;
1489*b7579f77SDag-Erling Smørgrav 	if(strcasecmp(name, "KeyDigest") == 0) {
1490*b7579f77SDag-Erling Smørgrav 		if(data->use_key)
1491*b7579f77SDag-Erling Smørgrav 			xml_append_ds(data);
1492*b7579f77SDag-Erling Smørgrav 		data->use_key = 0;
1493*b7579f77SDag-Erling Smørgrav 	} else if(strcasecmp(name, "Zone") == 0) {
1494*b7579f77SDag-Erling Smørgrav 		if(!xml_is_zone_name(data->czone, ".")) {
1495*b7579f77SDag-Erling Smørgrav 			if(verb) printf("xml not for the right zone\n");
1496*b7579f77SDag-Erling Smørgrav 			exit(0);
1497*b7579f77SDag-Erling Smørgrav 		}
1498*b7579f77SDag-Erling Smørgrav 	}
1499*b7579f77SDag-Erling Smørgrav }
1500*b7579f77SDag-Erling Smørgrav 
1501*b7579f77SDag-Erling Smørgrav /**
1502*b7579f77SDag-Erling Smørgrav  * XML parser setup of the callbacks for the tags
1503*b7579f77SDag-Erling Smørgrav  */
1504*b7579f77SDag-Erling Smørgrav static void
1505*b7579f77SDag-Erling Smørgrav xml_parse_setup(XML_Parser parser, struct xml_data* data, time_t now)
1506*b7579f77SDag-Erling Smørgrav {
1507*b7579f77SDag-Erling Smørgrav 	char buf[1024];
1508*b7579f77SDag-Erling Smørgrav 	memset(data, 0, sizeof(*data));
1509*b7579f77SDag-Erling Smørgrav 	XML_SetUserData(parser, data);
1510*b7579f77SDag-Erling Smørgrav 	data->parser = parser;
1511*b7579f77SDag-Erling Smørgrav 	data->date = now;
1512*b7579f77SDag-Erling Smørgrav 	data->ds = BIO_new(BIO_s_mem());
1513*b7579f77SDag-Erling Smørgrav 	data->ctag = BIO_new(BIO_s_mem());
1514*b7579f77SDag-Erling Smørgrav 	data->czone = BIO_new(BIO_s_mem());
1515*b7579f77SDag-Erling Smørgrav 	data->calgo = BIO_new(BIO_s_mem());
1516*b7579f77SDag-Erling Smørgrav 	data->cdigtype = BIO_new(BIO_s_mem());
1517*b7579f77SDag-Erling Smørgrav 	data->cdigest = BIO_new(BIO_s_mem());
1518*b7579f77SDag-Erling Smørgrav 	if(!data->ds || !data->ctag || !data->calgo || !data->czone ||
1519*b7579f77SDag-Erling Smørgrav 		!data->cdigtype || !data->cdigest) {
1520*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1521*b7579f77SDag-Erling Smørgrav 		exit(0);
1522*b7579f77SDag-Erling Smørgrav 	}
1523*b7579f77SDag-Erling Smørgrav 	snprintf(buf, sizeof(buf), "; created by unbound-anchor on %s",
1524*b7579f77SDag-Erling Smørgrav 		ctime(&now));
1525*b7579f77SDag-Erling Smørgrav 	if(BIO_write(data->ds, buf, (int)strlen(buf)) <= 0) {
1526*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1527*b7579f77SDag-Erling Smørgrav 		exit(0);
1528*b7579f77SDag-Erling Smørgrav 	}
1529*b7579f77SDag-Erling Smørgrav 	XML_SetElementHandler(parser, xml_startelem, xml_endelem);
1530*b7579f77SDag-Erling Smørgrav 	XML_SetCharacterDataHandler(parser, xml_charhandle);
1531*b7579f77SDag-Erling Smørgrav }
1532*b7579f77SDag-Erling Smørgrav 
1533*b7579f77SDag-Erling Smørgrav /**
1534*b7579f77SDag-Erling Smørgrav  * Perform XML parsing of the root-anchors file
1535*b7579f77SDag-Erling Smørgrav  * Its format description can be read here
1536*b7579f77SDag-Erling Smørgrav  * https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt
1537*b7579f77SDag-Erling Smørgrav  * It uses libexpat.
1538*b7579f77SDag-Erling Smørgrav  * @param xml: BIO with xml data.
1539*b7579f77SDag-Erling Smørgrav  * @param now: the current time for checking DS validity periods.
1540*b7579f77SDag-Erling Smørgrav  * @return memoryBIO with the DS data in zone format.
1541*b7579f77SDag-Erling Smørgrav  * 	or NULL if the zone is insecure.
1542*b7579f77SDag-Erling Smørgrav  * 	(It exit()s on error)
1543*b7579f77SDag-Erling Smørgrav  */
1544*b7579f77SDag-Erling Smørgrav static BIO*
1545*b7579f77SDag-Erling Smørgrav xml_parse(BIO* xml, time_t now)
1546*b7579f77SDag-Erling Smørgrav {
1547*b7579f77SDag-Erling Smørgrav 	char* pp;
1548*b7579f77SDag-Erling Smørgrav 	int len;
1549*b7579f77SDag-Erling Smørgrav 	XML_Parser parser;
1550*b7579f77SDag-Erling Smørgrav 	struct xml_data data;
1551*b7579f77SDag-Erling Smørgrav 
1552*b7579f77SDag-Erling Smørgrav 	parser = XML_ParserCreate(NULL);
1553*b7579f77SDag-Erling Smørgrav 	if(!parser) {
1554*b7579f77SDag-Erling Smørgrav 		if(verb) printf("could not XML_ParserCreate\n");
1555*b7579f77SDag-Erling Smørgrav 		exit(0);
1556*b7579f77SDag-Erling Smørgrav 	}
1557*b7579f77SDag-Erling Smørgrav 
1558*b7579f77SDag-Erling Smørgrav 	/* setup callbacks */
1559*b7579f77SDag-Erling Smørgrav 	xml_parse_setup(parser, &data, now);
1560*b7579f77SDag-Erling Smørgrav 
1561*b7579f77SDag-Erling Smørgrav 	/* parse it */
1562*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(xml);
1563*b7579f77SDag-Erling Smørgrav 	len = (int)BIO_get_mem_data(xml, &pp);
1564*b7579f77SDag-Erling Smørgrav 	if(!len || !pp) {
1565*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1566*b7579f77SDag-Erling Smørgrav 		exit(0);
1567*b7579f77SDag-Erling Smørgrav 	}
1568*b7579f77SDag-Erling Smørgrav 	if(!XML_Parse(parser, pp, len, 1 /*isfinal*/ )) {
1569*b7579f77SDag-Erling Smørgrav 		const char *e = XML_ErrorString(XML_GetErrorCode(parser));
1570*b7579f77SDag-Erling Smørgrav 		if(verb) printf("XML_Parse failure %s\n", e?e:"");
1571*b7579f77SDag-Erling Smørgrav 		exit(0);
1572*b7579f77SDag-Erling Smørgrav 	}
1573*b7579f77SDag-Erling Smørgrav 
1574*b7579f77SDag-Erling Smørgrav 	/* parsed */
1575*b7579f77SDag-Erling Smørgrav 	if(verb) printf("XML was parsed successfully, %d keys\n",
1576*b7579f77SDag-Erling Smørgrav 			data.num_keys);
1577*b7579f77SDag-Erling Smørgrav 	free(data.tag);
1578*b7579f77SDag-Erling Smørgrav 	XML_ParserFree(parser);
1579*b7579f77SDag-Erling Smørgrav 
1580*b7579f77SDag-Erling Smørgrav 	if(verb >= 4) {
1581*b7579f77SDag-Erling Smørgrav 		char* pp = NULL;
1582*b7579f77SDag-Erling Smørgrav 		int len;
1583*b7579f77SDag-Erling Smørgrav 		(void)BIO_seek(data.ds, 0);
1584*b7579f77SDag-Erling Smørgrav 		len = BIO_get_mem_data(data.ds, &pp);
1585*b7579f77SDag-Erling Smørgrav 		printf("got DS bio %d: '", len);
1586*b7579f77SDag-Erling Smørgrav 		if(!fwrite(pp, (size_t)len, 1, stdout))
1587*b7579f77SDag-Erling Smørgrav 			/* compilers do not allow us to ignore fwrite .. */
1588*b7579f77SDag-Erling Smørgrav 			fprintf(stderr, "error writing to stdout\n");
1589*b7579f77SDag-Erling Smørgrav 		printf("'\n");
1590*b7579f77SDag-Erling Smørgrav 	}
1591*b7579f77SDag-Erling Smørgrav 	BIO_free(data.czone);
1592*b7579f77SDag-Erling Smørgrav 	BIO_free(data.ctag);
1593*b7579f77SDag-Erling Smørgrav 	BIO_free(data.calgo);
1594*b7579f77SDag-Erling Smørgrav 	BIO_free(data.cdigtype);
1595*b7579f77SDag-Erling Smørgrav 	BIO_free(data.cdigest);
1596*b7579f77SDag-Erling Smørgrav 
1597*b7579f77SDag-Erling Smørgrav 	if(data.num_keys == 0) {
1598*b7579f77SDag-Erling Smørgrav 		/* the root zone seems to have gone insecure */
1599*b7579f77SDag-Erling Smørgrav 		BIO_free(data.ds);
1600*b7579f77SDag-Erling Smørgrav 		return NULL;
1601*b7579f77SDag-Erling Smørgrav 	} else {
1602*b7579f77SDag-Erling Smørgrav 		return data.ds;
1603*b7579f77SDag-Erling Smørgrav 	}
1604*b7579f77SDag-Erling Smørgrav }
1605*b7579f77SDag-Erling Smørgrav 
1606*b7579f77SDag-Erling Smørgrav /** verify a PKCS7 signature, false on failure */
1607*b7579f77SDag-Erling Smørgrav static int
1608*b7579f77SDag-Erling Smørgrav verify_p7sig(BIO* data, BIO* p7s, STACK_OF(X509)* trust)
1609*b7579f77SDag-Erling Smørgrav {
1610*b7579f77SDag-Erling Smørgrav 	PKCS7* p7;
1611*b7579f77SDag-Erling Smørgrav 	X509_STORE *store = X509_STORE_new();
1612*b7579f77SDag-Erling Smørgrav 	int secure = 0;
1613*b7579f77SDag-Erling Smørgrav 	int i;
1614*b7579f77SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE
1615*b7579f77SDag-Erling Smørgrav 	X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new();
1616*b7579f77SDag-Erling Smørgrav 	if(!param) {
1617*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1618*b7579f77SDag-Erling Smørgrav 		X509_STORE_free(store);
1619*b7579f77SDag-Erling Smørgrav 		return 0;
1620*b7579f77SDag-Erling Smørgrav 	}
1621*b7579f77SDag-Erling Smørgrav 	/* do the selfcheck on the root certificate; it checks that the
1622*b7579f77SDag-Erling Smørgrav 	 * input is valid */
1623*b7579f77SDag-Erling Smørgrav 	X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CHECK_SS_SIGNATURE);
1624*b7579f77SDag-Erling Smørgrav 	if(store) X509_STORE_set1_param(store, param);
1625*b7579f77SDag-Erling Smørgrav #endif
1626*b7579f77SDag-Erling Smørgrav 	if(!store) {
1627*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1628*b7579f77SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE
1629*b7579f77SDag-Erling Smørgrav 		X509_VERIFY_PARAM_free(param);
1630*b7579f77SDag-Erling Smørgrav #endif
1631*b7579f77SDag-Erling Smørgrav 		return 0;
1632*b7579f77SDag-Erling Smørgrav 	}
1633*b7579f77SDag-Erling Smørgrav 
1634*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(p7s);
1635*b7579f77SDag-Erling Smørgrav 	(void)BIO_reset(data);
1636*b7579f77SDag-Erling Smørgrav 
1637*b7579f77SDag-Erling Smørgrav 	/* convert p7s to p7 (the signature) */
1638*b7579f77SDag-Erling Smørgrav 	p7 = d2i_PKCS7_bio(p7s, NULL);
1639*b7579f77SDag-Erling Smørgrav 	if(!p7) {
1640*b7579f77SDag-Erling Smørgrav 		if(verb) printf("could not parse p7s signature file\n");
1641*b7579f77SDag-Erling Smørgrav 		X509_STORE_free(store);
1642*b7579f77SDag-Erling Smørgrav 		return 0;
1643*b7579f77SDag-Erling Smørgrav 	}
1644*b7579f77SDag-Erling Smørgrav 	if(verb >= 2) printf("parsed the PKCS7 signature\n");
1645*b7579f77SDag-Erling Smørgrav 
1646*b7579f77SDag-Erling Smørgrav 	/* convert trust to trusted certificate store */
1647*b7579f77SDag-Erling Smørgrav 	for(i=0; i<sk_X509_num(trust); i++) {
1648*b7579f77SDag-Erling Smørgrav 		if(!X509_STORE_add_cert(store, sk_X509_value(trust, i))) {
1649*b7579f77SDag-Erling Smørgrav 			if(verb) printf("failed X509_STORE_add_cert\n");
1650*b7579f77SDag-Erling Smørgrav 			X509_STORE_free(store);
1651*b7579f77SDag-Erling Smørgrav 			PKCS7_free(p7);
1652*b7579f77SDag-Erling Smørgrav 			return 0;
1653*b7579f77SDag-Erling Smørgrav 		}
1654*b7579f77SDag-Erling Smørgrav 	}
1655*b7579f77SDag-Erling Smørgrav 	if(verb >= 2) printf("setup the X509_STORE\n");
1656*b7579f77SDag-Erling Smørgrav 
1657*b7579f77SDag-Erling Smørgrav 	if(PKCS7_verify(p7, NULL, store, data, NULL, 0) == 1) {
1658*b7579f77SDag-Erling Smørgrav 		secure = 1;
1659*b7579f77SDag-Erling Smørgrav 		if(verb) printf("the PKCS7 signature verified\n");
1660*b7579f77SDag-Erling Smørgrav 	} else {
1661*b7579f77SDag-Erling Smørgrav 		if(verb) {
1662*b7579f77SDag-Erling Smørgrav 			ERR_print_errors_fp(stdout);
1663*b7579f77SDag-Erling Smørgrav 		}
1664*b7579f77SDag-Erling Smørgrav 	}
1665*b7579f77SDag-Erling Smørgrav 
1666*b7579f77SDag-Erling Smørgrav 	X509_STORE_free(store);
1667*b7579f77SDag-Erling Smørgrav 	PKCS7_free(p7);
1668*b7579f77SDag-Erling Smørgrav 	return secure;
1669*b7579f77SDag-Erling Smørgrav }
1670*b7579f77SDag-Erling Smørgrav 
1671*b7579f77SDag-Erling Smørgrav /** write unsigned root anchor file, a 5011 revoked tp */
1672*b7579f77SDag-Erling Smørgrav static void
1673*b7579f77SDag-Erling Smørgrav write_unsigned_root(char* root_anchor_file)
1674*b7579f77SDag-Erling Smørgrav {
1675*b7579f77SDag-Erling Smørgrav 	FILE* out;
1676*b7579f77SDag-Erling Smørgrav 	time_t now = time(NULL);
1677*b7579f77SDag-Erling Smørgrav 	out = fopen(root_anchor_file, "w");
1678*b7579f77SDag-Erling Smørgrav 	if(!out) {
1679*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno));
1680*b7579f77SDag-Erling Smørgrav 		return;
1681*b7579f77SDag-Erling Smørgrav 	}
1682*b7579f77SDag-Erling Smørgrav 	if(fprintf(out, "; autotrust trust anchor file\n"
1683*b7579f77SDag-Erling Smørgrav 		";;REVOKED\n"
1684*b7579f77SDag-Erling Smørgrav 		";;id: . 1\n"
1685*b7579f77SDag-Erling Smørgrav 		"; This file was written by unbound-anchor on %s"
1686*b7579f77SDag-Erling Smørgrav 		"; It indicates that the root does not use DNSSEC\n"
1687*b7579f77SDag-Erling Smørgrav 		"; to restart DNSSEC overwrite this file with a\n"
1688*b7579f77SDag-Erling Smørgrav 		"; valid trustanchor or (empty-it and run unbound-anchor)\n"
1689*b7579f77SDag-Erling Smørgrav 		, ctime(&now)) < 0) {
1690*b7579f77SDag-Erling Smørgrav 		if(verb) printf("failed to write 'unsigned' to %s\n",
1691*b7579f77SDag-Erling Smørgrav 			root_anchor_file);
1692*b7579f77SDag-Erling Smørgrav 		if(verb && errno != 0) printf("%s\n", strerror(errno));
1693*b7579f77SDag-Erling Smørgrav 	}
1694*b7579f77SDag-Erling Smørgrav 	fclose(out);
1695*b7579f77SDag-Erling Smørgrav }
1696*b7579f77SDag-Erling Smørgrav 
1697*b7579f77SDag-Erling Smørgrav /** write root anchor file */
1698*b7579f77SDag-Erling Smørgrav static void
1699*b7579f77SDag-Erling Smørgrav write_root_anchor(char* root_anchor_file, BIO* ds)
1700*b7579f77SDag-Erling Smørgrav {
1701*b7579f77SDag-Erling Smørgrav 	char* pp = NULL;
1702*b7579f77SDag-Erling Smørgrav 	int len;
1703*b7579f77SDag-Erling Smørgrav 	FILE* out;
1704*b7579f77SDag-Erling Smørgrav 	(void)BIO_seek(ds, 0);
1705*b7579f77SDag-Erling Smørgrav 	len = BIO_get_mem_data(ds, &pp);
1706*b7579f77SDag-Erling Smørgrav 	if(!len || !pp) {
1707*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1708*b7579f77SDag-Erling Smørgrav 		return;
1709*b7579f77SDag-Erling Smørgrav 	}
1710*b7579f77SDag-Erling Smørgrav 	out = fopen(root_anchor_file, "w");
1711*b7579f77SDag-Erling Smørgrav 	if(!out) {
1712*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno));
1713*b7579f77SDag-Erling Smørgrav 		return;
1714*b7579f77SDag-Erling Smørgrav 	}
1715*b7579f77SDag-Erling Smørgrav 	if(fwrite(pp, (size_t)len, 1, out) != 1) {
1716*b7579f77SDag-Erling Smørgrav 		if(verb) printf("failed to write all data to %s\n",
1717*b7579f77SDag-Erling Smørgrav 			root_anchor_file);
1718*b7579f77SDag-Erling Smørgrav 		if(verb && errno != 0) printf("%s\n", strerror(errno));
1719*b7579f77SDag-Erling Smørgrav 	}
1720*b7579f77SDag-Erling Smørgrav 	fclose(out);
1721*b7579f77SDag-Erling Smørgrav }
1722*b7579f77SDag-Erling Smørgrav 
1723*b7579f77SDag-Erling Smørgrav /** Perform the verification and update of the trustanchor file */
1724*b7579f77SDag-Erling Smørgrav static void
1725*b7579f77SDag-Erling Smørgrav verify_and_update_anchor(char* root_anchor_file, BIO* xml, BIO* p7s,
1726*b7579f77SDag-Erling Smørgrav 	STACK_OF(X509)* cert)
1727*b7579f77SDag-Erling Smørgrav {
1728*b7579f77SDag-Erling Smørgrav 	BIO* ds;
1729*b7579f77SDag-Erling Smørgrav 
1730*b7579f77SDag-Erling Smørgrav 	/* verify xml file */
1731*b7579f77SDag-Erling Smørgrav 	if(!verify_p7sig(xml, p7s, cert)) {
1732*b7579f77SDag-Erling Smørgrav 		printf("the PKCS7 signature failed\n");
1733*b7579f77SDag-Erling Smørgrav 		exit(0);
1734*b7579f77SDag-Erling Smørgrav 	}
1735*b7579f77SDag-Erling Smørgrav 
1736*b7579f77SDag-Erling Smørgrav 	/* parse the xml file into DS records */
1737*b7579f77SDag-Erling Smørgrav 	ds = xml_parse(xml, time(NULL));
1738*b7579f77SDag-Erling Smørgrav 	if(!ds) {
1739*b7579f77SDag-Erling Smørgrav 		/* the root zone is unsigned now */
1740*b7579f77SDag-Erling Smørgrav 		write_unsigned_root(root_anchor_file);
1741*b7579f77SDag-Erling Smørgrav 	} else {
1742*b7579f77SDag-Erling Smørgrav 		/* reinstate 5011 tracking */
1743*b7579f77SDag-Erling Smørgrav 		write_root_anchor(root_anchor_file, ds);
1744*b7579f77SDag-Erling Smørgrav 	}
1745*b7579f77SDag-Erling Smørgrav 	BIO_free(ds);
1746*b7579f77SDag-Erling Smørgrav }
1747*b7579f77SDag-Erling Smørgrav 
1748*b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK
1749*b7579f77SDag-Erling Smørgrav static void do_wsa_cleanup(void) { WSACleanup(); }
1750*b7579f77SDag-Erling Smørgrav #endif
1751*b7579f77SDag-Erling Smørgrav 
1752*b7579f77SDag-Erling Smørgrav /** perform actual certupdate work */
1753*b7579f77SDag-Erling Smørgrav static int
1754*b7579f77SDag-Erling Smørgrav do_certupdate(char* root_anchor_file, char* root_cert_file,
1755*b7579f77SDag-Erling Smørgrav 	char* urlname, char* xmlname, char* p7sname,
1756*b7579f77SDag-Erling Smørgrav 	char* res_conf, char* root_hints, char* debugconf,
1757*b7579f77SDag-Erling Smørgrav 	int ip4only, int ip6only, int port, struct ub_result* dnskey)
1758*b7579f77SDag-Erling Smørgrav {
1759*b7579f77SDag-Erling Smørgrav 	STACK_OF(X509)* cert;
1760*b7579f77SDag-Erling Smørgrav 	BIO *xml, *p7s;
1761*b7579f77SDag-Erling Smørgrav 	struct ip_list* ip_list = NULL;
1762*b7579f77SDag-Erling Smørgrav 
1763*b7579f77SDag-Erling Smørgrav 	/* read pem file or provide builtin */
1764*b7579f77SDag-Erling Smørgrav 	cert = read_cert_or_builtin(root_cert_file);
1765*b7579f77SDag-Erling Smørgrav 
1766*b7579f77SDag-Erling Smørgrav 	/* lookup A, AAAA for the urlname (or parse urlname if IP address) */
1767*b7579f77SDag-Erling Smørgrav 	ip_list = resolve_name(urlname, port, res_conf, root_hints, debugconf,
1768*b7579f77SDag-Erling Smørgrav 		ip4only, ip6only);
1769*b7579f77SDag-Erling Smørgrav 
1770*b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK
1771*b7579f77SDag-Erling Smørgrav 	if(1) { /* libunbound finished, startup WSA for the https connection */
1772*b7579f77SDag-Erling Smørgrav 		WSADATA wsa_data;
1773*b7579f77SDag-Erling Smørgrav 		int r;
1774*b7579f77SDag-Erling Smørgrav 		if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) {
1775*b7579f77SDag-Erling Smørgrav 			if(verb) printf("WSAStartup failed: %s\n",
1776*b7579f77SDag-Erling Smørgrav 				wsa_strerror(r));
1777*b7579f77SDag-Erling Smørgrav 			exit(0);
1778*b7579f77SDag-Erling Smørgrav 		}
1779*b7579f77SDag-Erling Smørgrav 		atexit(&do_wsa_cleanup);
1780*b7579f77SDag-Erling Smørgrav 	}
1781*b7579f77SDag-Erling Smørgrav #endif
1782*b7579f77SDag-Erling Smørgrav 
1783*b7579f77SDag-Erling Smørgrav 	/* fetch the necessary files over HTTPS */
1784*b7579f77SDag-Erling Smørgrav 	xml = https(ip_list, xmlname, urlname);
1785*b7579f77SDag-Erling Smørgrav 	p7s = https(ip_list, p7sname, urlname);
1786*b7579f77SDag-Erling Smørgrav 
1787*b7579f77SDag-Erling Smørgrav 	/* verify and update the root anchor */
1788*b7579f77SDag-Erling Smørgrav 	verify_and_update_anchor(root_anchor_file, xml, p7s, cert);
1789*b7579f77SDag-Erling Smørgrav 	if(verb) printf("success: the anchor has been updated "
1790*b7579f77SDag-Erling Smørgrav 			"using the cert\n");
1791*b7579f77SDag-Erling Smørgrav 
1792*b7579f77SDag-Erling Smørgrav 	free_file_bio(xml);
1793*b7579f77SDag-Erling Smørgrav 	free_file_bio(p7s);
1794*b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S
1795*b7579f77SDag-Erling Smørgrav 	sk_X509_pop_free(cert, X509_free);
1796*b7579f77SDag-Erling Smørgrav #endif
1797*b7579f77SDag-Erling Smørgrav 	ub_resolve_free(dnskey);
1798*b7579f77SDag-Erling Smørgrav 	ip_list_free(ip_list);
1799*b7579f77SDag-Erling Smørgrav 	return 1;
1800*b7579f77SDag-Erling Smørgrav }
1801*b7579f77SDag-Erling Smørgrav 
1802*b7579f77SDag-Erling Smørgrav /**
1803*b7579f77SDag-Erling Smørgrav  * Try to read the root RFC5011 autotrust anchor file,
1804*b7579f77SDag-Erling Smørgrav  * @param file: filename.
1805*b7579f77SDag-Erling Smørgrav  * @return:
1806*b7579f77SDag-Erling Smørgrav  * 	0 if does not exist or empty
1807*b7579f77SDag-Erling Smørgrav  * 	1 if trust-point-revoked-5011
1808*b7579f77SDag-Erling Smørgrav  * 	2 if it is OK.
1809*b7579f77SDag-Erling Smørgrav  */
1810*b7579f77SDag-Erling Smørgrav static int
1811*b7579f77SDag-Erling Smørgrav try_read_anchor(char* file)
1812*b7579f77SDag-Erling Smørgrav {
1813*b7579f77SDag-Erling Smørgrav 	int empty = 1;
1814*b7579f77SDag-Erling Smørgrav 	char line[10240];
1815*b7579f77SDag-Erling Smørgrav 	char* p;
1816*b7579f77SDag-Erling Smørgrav 	FILE* in = fopen(file, "r");
1817*b7579f77SDag-Erling Smørgrav 	if(!in) {
1818*b7579f77SDag-Erling Smørgrav 		/* only if the file does not exist, can we fix it */
1819*b7579f77SDag-Erling Smørgrav 		if(errno != ENOENT) {
1820*b7579f77SDag-Erling Smørgrav 			if(verb) printf("%s: %s\n", file, strerror(errno));
1821*b7579f77SDag-Erling Smørgrav 			if(verb) printf("error: cannot access the file\n");
1822*b7579f77SDag-Erling Smørgrav 			exit(0);
1823*b7579f77SDag-Erling Smørgrav 		}
1824*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s does not exist\n", file);
1825*b7579f77SDag-Erling Smørgrav 		return 0;
1826*b7579f77SDag-Erling Smørgrav 	}
1827*b7579f77SDag-Erling Smørgrav 	while(fgets(line, (int)sizeof(line), in)) {
1828*b7579f77SDag-Erling Smørgrav 		line[sizeof(line)-1] = 0;
1829*b7579f77SDag-Erling Smørgrav 		if(strncmp(line, ";;REVOKED", 9) == 0) {
1830*b7579f77SDag-Erling Smørgrav 			fclose(in);
1831*b7579f77SDag-Erling Smørgrav 			if(verb) printf("%s : the trust point is revoked\n"
1832*b7579f77SDag-Erling Smørgrav 				"and the zone is considered unsigned.\n"
1833*b7579f77SDag-Erling Smørgrav 				"if you wish to re-enable, delete the file\n",
1834*b7579f77SDag-Erling Smørgrav 				file);
1835*b7579f77SDag-Erling Smørgrav 			return 1;
1836*b7579f77SDag-Erling Smørgrav 		}
1837*b7579f77SDag-Erling Smørgrav 		p=line;
1838*b7579f77SDag-Erling Smørgrav 		while(*p == ' ' || *p == '\t')
1839*b7579f77SDag-Erling Smørgrav 			p++;
1840*b7579f77SDag-Erling Smørgrav 		if(p[0]==0 || p[0]=='\n' || p[0]==';') continue;
1841*b7579f77SDag-Erling Smørgrav 		/* this line is a line of content */
1842*b7579f77SDag-Erling Smørgrav 		empty = 0;
1843*b7579f77SDag-Erling Smørgrav 	}
1844*b7579f77SDag-Erling Smørgrav 	fclose(in);
1845*b7579f77SDag-Erling Smørgrav 	if(empty) {
1846*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s is empty\n", file);
1847*b7579f77SDag-Erling Smørgrav 		return 0;
1848*b7579f77SDag-Erling Smørgrav 	}
1849*b7579f77SDag-Erling Smørgrav 	if(verb) printf("%s has content\n", file);
1850*b7579f77SDag-Erling Smørgrav 	return 2;
1851*b7579f77SDag-Erling Smørgrav }
1852*b7579f77SDag-Erling Smørgrav 
1853*b7579f77SDag-Erling Smørgrav /** Write the builtin root anchor to a file */
1854*b7579f77SDag-Erling Smørgrav static void
1855*b7579f77SDag-Erling Smørgrav write_builtin_anchor(char* file)
1856*b7579f77SDag-Erling Smørgrav {
1857*b7579f77SDag-Erling Smørgrav 	const char* builtin_root_anchor = get_builtin_ds();
1858*b7579f77SDag-Erling Smørgrav 	FILE* out = fopen(file, "w");
1859*b7579f77SDag-Erling Smørgrav 	if(!out) {
1860*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s: %s\n", file, strerror(errno));
1861*b7579f77SDag-Erling Smørgrav 		if(verb) printf("  could not write builtin anchor\n");
1862*b7579f77SDag-Erling Smørgrav 		return;
1863*b7579f77SDag-Erling Smørgrav 	}
1864*b7579f77SDag-Erling Smørgrav 	if(!fwrite(builtin_root_anchor, strlen(builtin_root_anchor), 1, out)) {
1865*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s: %s\n", file, strerror(errno));
1866*b7579f77SDag-Erling Smørgrav 		if(verb) printf("  could not complete write builtin anchor\n");
1867*b7579f77SDag-Erling Smørgrav 	}
1868*b7579f77SDag-Erling Smørgrav 	fclose(out);
1869*b7579f77SDag-Erling Smørgrav }
1870*b7579f77SDag-Erling Smørgrav 
1871*b7579f77SDag-Erling Smørgrav /**
1872*b7579f77SDag-Erling Smørgrav  * Check the root anchor file.
1873*b7579f77SDag-Erling Smørgrav  * If does not exist, provide builtin and write file.
1874*b7579f77SDag-Erling Smørgrav  * If empty, provide builtin and write file.
1875*b7579f77SDag-Erling Smørgrav  * If trust-point-revoked-5011 file: make the program exit.
1876*b7579f77SDag-Erling Smørgrav  * @param root_anchor_file: filename of the root anchor.
1877*b7579f77SDag-Erling Smørgrav  * @param used_builtin: set to 1 if the builtin is written.
1878*b7579f77SDag-Erling Smørgrav  * @return 0 if trustpoint is insecure, 1 on success.  Exit on failure.
1879*b7579f77SDag-Erling Smørgrav  */
1880*b7579f77SDag-Erling Smørgrav static int
1881*b7579f77SDag-Erling Smørgrav provide_builtin(char* root_anchor_file, int* used_builtin)
1882*b7579f77SDag-Erling Smørgrav {
1883*b7579f77SDag-Erling Smørgrav 	/* try to read it */
1884*b7579f77SDag-Erling Smørgrav 	switch(try_read_anchor(root_anchor_file))
1885*b7579f77SDag-Erling Smørgrav 	{
1886*b7579f77SDag-Erling Smørgrav 		case 0: /* no exist or empty */
1887*b7579f77SDag-Erling Smørgrav 			write_builtin_anchor(root_anchor_file);
1888*b7579f77SDag-Erling Smørgrav 			*used_builtin = 1;
1889*b7579f77SDag-Erling Smørgrav 			break;
1890*b7579f77SDag-Erling Smørgrav 		case 1: /* revoked tp */
1891*b7579f77SDag-Erling Smørgrav 			return 0;
1892*b7579f77SDag-Erling Smørgrav 		case 2: /* it is fine */
1893*b7579f77SDag-Erling Smørgrav 		default:
1894*b7579f77SDag-Erling Smørgrav 			break;
1895*b7579f77SDag-Erling Smørgrav 	}
1896*b7579f77SDag-Erling Smørgrav 	return 1;
1897*b7579f77SDag-Erling Smørgrav }
1898*b7579f77SDag-Erling Smørgrav 
1899*b7579f77SDag-Erling Smørgrav /**
1900*b7579f77SDag-Erling Smørgrav  * add an autotrust anchor for the root to the context
1901*b7579f77SDag-Erling Smørgrav  */
1902*b7579f77SDag-Erling Smørgrav static void
1903*b7579f77SDag-Erling Smørgrav add_5011_probe_root(struct ub_ctx* ctx, char* root_anchor_file)
1904*b7579f77SDag-Erling Smørgrav {
1905*b7579f77SDag-Erling Smørgrav 	int r;
1906*b7579f77SDag-Erling Smørgrav 	r = ub_ctx_set_option(ctx, "auto-trust-anchor-file:", root_anchor_file);
1907*b7579f77SDag-Erling Smørgrav 	if(r) {
1908*b7579f77SDag-Erling Smørgrav 		if(verb) printf("add 5011 probe to ctx: %s\n", ub_strerror(r));
1909*b7579f77SDag-Erling Smørgrav 		ub_ctx_delete(ctx);
1910*b7579f77SDag-Erling Smørgrav 		exit(0);
1911*b7579f77SDag-Erling Smørgrav 	}
1912*b7579f77SDag-Erling Smørgrav }
1913*b7579f77SDag-Erling Smørgrav 
1914*b7579f77SDag-Erling Smørgrav /**
1915*b7579f77SDag-Erling Smørgrav  * Prime the root key and return the result.  Exit on error.
1916*b7579f77SDag-Erling Smørgrav  * @param ctx: the unbound context to perform the priming with.
1917*b7579f77SDag-Erling Smørgrav  * @return: the result of the prime, on error it exit()s.
1918*b7579f77SDag-Erling Smørgrav  */
1919*b7579f77SDag-Erling Smørgrav static struct ub_result*
1920*b7579f77SDag-Erling Smørgrav prime_root_key(struct ub_ctx* ctx)
1921*b7579f77SDag-Erling Smørgrav {
1922*b7579f77SDag-Erling Smørgrav 	struct ub_result* res = NULL;
1923*b7579f77SDag-Erling Smørgrav 	int r;
1924*b7579f77SDag-Erling Smørgrav 	r = ub_resolve(ctx, ".", LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, &res);
1925*b7579f77SDag-Erling Smørgrav 	if(r) {
1926*b7579f77SDag-Erling Smørgrav 		if(verb) printf("resolve DNSKEY: %s\n", ub_strerror(r));
1927*b7579f77SDag-Erling Smørgrav 		ub_ctx_delete(ctx);
1928*b7579f77SDag-Erling Smørgrav 		exit(0);
1929*b7579f77SDag-Erling Smørgrav 	}
1930*b7579f77SDag-Erling Smørgrav 	if(!res) {
1931*b7579f77SDag-Erling Smørgrav 		if(verb) printf("out of memory\n");
1932*b7579f77SDag-Erling Smørgrav 		ub_ctx_delete(ctx);
1933*b7579f77SDag-Erling Smørgrav 		exit(0);
1934*b7579f77SDag-Erling Smørgrav 	}
1935*b7579f77SDag-Erling Smørgrav 	return res;
1936*b7579f77SDag-Erling Smørgrav }
1937*b7579f77SDag-Erling Smørgrav 
1938*b7579f77SDag-Erling Smørgrav /** see if ADDPEND keys exist in autotrust file (if possible) */
1939*b7579f77SDag-Erling Smørgrav static int
1940*b7579f77SDag-Erling Smørgrav read_if_pending_keys(char* file)
1941*b7579f77SDag-Erling Smørgrav {
1942*b7579f77SDag-Erling Smørgrav 	FILE* in = fopen(file, "r");
1943*b7579f77SDag-Erling Smørgrav 	char line[8192];
1944*b7579f77SDag-Erling Smørgrav 	if(!in) {
1945*b7579f77SDag-Erling Smørgrav 		if(verb>=2) printf("%s: %s\n", file, strerror(errno));
1946*b7579f77SDag-Erling Smørgrav 		return 0;
1947*b7579f77SDag-Erling Smørgrav 	}
1948*b7579f77SDag-Erling Smørgrav 	while(fgets(line, (int)sizeof(line), in)) {
1949*b7579f77SDag-Erling Smørgrav 		if(line[0]==';') continue;
1950*b7579f77SDag-Erling Smørgrav 		if(strstr(line, "[ ADDPEND ]")) {
1951*b7579f77SDag-Erling Smørgrav 			fclose(in);
1952*b7579f77SDag-Erling Smørgrav 			if(verb) printf("RFC5011-state has ADDPEND keys\n");
1953*b7579f77SDag-Erling Smørgrav 			return 1;
1954*b7579f77SDag-Erling Smørgrav 		}
1955*b7579f77SDag-Erling Smørgrav 	}
1956*b7579f77SDag-Erling Smørgrav 	fclose(in);
1957*b7579f77SDag-Erling Smørgrav 	return 0;
1958*b7579f77SDag-Erling Smørgrav }
1959*b7579f77SDag-Erling Smørgrav 
1960*b7579f77SDag-Erling Smørgrav /** read last successful probe time from autotrust file (if possible) */
1961*b7579f77SDag-Erling Smørgrav static int32_t
1962*b7579f77SDag-Erling Smørgrav read_last_success_time(char* file)
1963*b7579f77SDag-Erling Smørgrav {
1964*b7579f77SDag-Erling Smørgrav 	FILE* in = fopen(file, "r");
1965*b7579f77SDag-Erling Smørgrav 	char line[1024];
1966*b7579f77SDag-Erling Smørgrav 	if(!in) {
1967*b7579f77SDag-Erling Smørgrav 		if(verb) printf("%s: %s\n", file, strerror(errno));
1968*b7579f77SDag-Erling Smørgrav 		return 0;
1969*b7579f77SDag-Erling Smørgrav 	}
1970*b7579f77SDag-Erling Smørgrav 	while(fgets(line, (int)sizeof(line), in)) {
1971*b7579f77SDag-Erling Smørgrav 		if(strncmp(line, ";;last_success: ", 16) == 0) {
1972*b7579f77SDag-Erling Smørgrav 			char* e;
1973*b7579f77SDag-Erling Smørgrav 			time_t x = (unsigned int)strtol(line+16, &e, 10);
1974*b7579f77SDag-Erling Smørgrav 			fclose(in);
1975*b7579f77SDag-Erling Smørgrav 			if(line+16 == e) {
1976*b7579f77SDag-Erling Smørgrav 				if(verb) printf("failed to parse "
1977*b7579f77SDag-Erling Smørgrav 					"last_success probe time\n");
1978*b7579f77SDag-Erling Smørgrav 				return 0;
1979*b7579f77SDag-Erling Smørgrav 			}
1980*b7579f77SDag-Erling Smørgrav 			if(verb) printf("last successful probe: %s", ctime(&x));
1981*b7579f77SDag-Erling Smørgrav 			return (int32_t)x;
1982*b7579f77SDag-Erling Smørgrav 		}
1983*b7579f77SDag-Erling Smørgrav 	}
1984*b7579f77SDag-Erling Smørgrav 	fclose(in);
1985*b7579f77SDag-Erling Smørgrav 	if(verb) printf("no last_success probe time in anchor file\n");
1986*b7579f77SDag-Erling Smørgrav 	return 0;
1987*b7579f77SDag-Erling Smørgrav }
1988*b7579f77SDag-Erling Smørgrav 
1989*b7579f77SDag-Erling Smørgrav /**
1990*b7579f77SDag-Erling Smørgrav  * Read autotrust 5011 probe file and see if the date
1991*b7579f77SDag-Erling Smørgrav  * compared to the current date allows a certupdate.
1992*b7579f77SDag-Erling Smørgrav  * If the last successful probe was recent then 5011 cannot be behind,
1993*b7579f77SDag-Erling Smørgrav  * and the failure cannot be solved with a certupdate.
1994*b7579f77SDag-Erling Smørgrav  * The debugconf is to validation-override the date for testing.
1995*b7579f77SDag-Erling Smørgrav  * @param root_anchor_file: filename of root key
1996*b7579f77SDag-Erling Smørgrav  * @return true if certupdate is ok.
1997*b7579f77SDag-Erling Smørgrav  */
1998*b7579f77SDag-Erling Smørgrav static int
1999*b7579f77SDag-Erling Smørgrav probe_date_allows_certupdate(char* root_anchor_file)
2000*b7579f77SDag-Erling Smørgrav {
2001*b7579f77SDag-Erling Smørgrav 	int has_pending_keys = read_if_pending_keys(root_anchor_file);
2002*b7579f77SDag-Erling Smørgrav 	int32_t last_success = read_last_success_time(root_anchor_file);
2003*b7579f77SDag-Erling Smørgrav 	int32_t now = (int32_t)time(NULL);
2004*b7579f77SDag-Erling Smørgrav 	int32_t leeway = 30 * 24 * 3600; /* 30 days leeway */
2005*b7579f77SDag-Erling Smørgrav 	/* if the date is before 2010-07-15:00.00.00 then the root has not
2006*b7579f77SDag-Erling Smørgrav 	 * been signed yet, and thus we refuse to take action. */
2007*b7579f77SDag-Erling Smørgrav 	if(time(NULL) < xml_convertdate("2010-07-15T00:00:00")) {
2008*b7579f77SDag-Erling Smørgrav 		if(verb) printf("the date is before the root was first signed,"
2009*b7579f77SDag-Erling Smørgrav 			" please correct the clock\n");
2010*b7579f77SDag-Erling Smørgrav 		return 0;
2011*b7579f77SDag-Erling Smørgrav 	}
2012*b7579f77SDag-Erling Smørgrav 	if(last_success == 0)
2013*b7579f77SDag-Erling Smørgrav 		return 1; /* no probe time */
2014*b7579f77SDag-Erling Smørgrav 	if(has_pending_keys)
2015*b7579f77SDag-Erling Smørgrav 		return 1; /* key in ADDPEND state, a previous probe has
2016*b7579f77SDag-Erling Smørgrav 		inserted that, and it was present in all recent probes,
2017*b7579f77SDag-Erling Smørgrav 		but it has not become active.  The 30 day timer may not have
2018*b7579f77SDag-Erling Smørgrav 		expired, but we know(for sure) there is a rollover going on.
2019*b7579f77SDag-Erling Smørgrav 		If we only managed to pickup the new key on its last day
2020*b7579f77SDag-Erling Smørgrav 		of announcement (for example) this can happen. */
2021*b7579f77SDag-Erling Smørgrav 	if(now - last_success < 0) {
2022*b7579f77SDag-Erling Smørgrav 		if(verb) printf("the last successful probe is in the future,"
2023*b7579f77SDag-Erling Smørgrav 			" clock was modified\n");
2024*b7579f77SDag-Erling Smørgrav 		return 0;
2025*b7579f77SDag-Erling Smørgrav 	}
2026*b7579f77SDag-Erling Smørgrav 	if(now - last_success >= leeway) {
2027*b7579f77SDag-Erling Smørgrav 		if(verb) printf("the last successful probe was more than 30 "
2028*b7579f77SDag-Erling Smørgrav 			"days ago\n");
2029*b7579f77SDag-Erling Smørgrav 		return 1;
2030*b7579f77SDag-Erling Smørgrav 	}
2031*b7579f77SDag-Erling Smørgrav 	if(verb) printf("the last successful probe is recent\n");
2032*b7579f77SDag-Erling Smørgrav 	return 0;
2033*b7579f77SDag-Erling Smørgrav }
2034*b7579f77SDag-Erling Smørgrav 
2035*b7579f77SDag-Erling Smørgrav /** perform the unbound-anchor work */
2036*b7579f77SDag-Erling Smørgrav static int
2037*b7579f77SDag-Erling Smørgrav do_root_update_work(char* root_anchor_file, char* root_cert_file,
2038*b7579f77SDag-Erling Smørgrav 	char* urlname, char* xmlname, char* p7sname,
2039*b7579f77SDag-Erling Smørgrav 	char* res_conf, char* root_hints, char* debugconf,
2040*b7579f77SDag-Erling Smørgrav 	int ip4only, int ip6only, int force, int port)
2041*b7579f77SDag-Erling Smørgrav {
2042*b7579f77SDag-Erling Smørgrav 	struct ub_ctx* ctx;
2043*b7579f77SDag-Erling Smørgrav 	struct ub_result* dnskey;
2044*b7579f77SDag-Erling Smørgrav 	int used_builtin = 0;
2045*b7579f77SDag-Erling Smørgrav 
2046*b7579f77SDag-Erling Smørgrav 	/* see if builtin rootanchor needs to be provided, or if
2047*b7579f77SDag-Erling Smørgrav 	 * rootanchor is 'revoked-trust-point' */
2048*b7579f77SDag-Erling Smørgrav 	if(!provide_builtin(root_anchor_file, &used_builtin))
2049*b7579f77SDag-Erling Smørgrav 		return 0;
2050*b7579f77SDag-Erling Smørgrav 
2051*b7579f77SDag-Erling Smørgrav 	/* make unbound context with 5011-probe for root anchor,
2052*b7579f77SDag-Erling Smørgrav 	 * and probe . DNSKEY */
2053*b7579f77SDag-Erling Smørgrav 	ctx = create_unbound_context(res_conf, root_hints, debugconf,
2054*b7579f77SDag-Erling Smørgrav 		ip4only, ip6only);
2055*b7579f77SDag-Erling Smørgrav 	add_5011_probe_root(ctx, root_anchor_file);
2056*b7579f77SDag-Erling Smørgrav 	dnskey = prime_root_key(ctx);
2057*b7579f77SDag-Erling Smørgrav 	ub_ctx_delete(ctx);
2058*b7579f77SDag-Erling Smørgrav 
2059*b7579f77SDag-Erling Smørgrav 	/* if secure: exit */
2060*b7579f77SDag-Erling Smørgrav 	if(dnskey->secure && !force) {
2061*b7579f77SDag-Erling Smørgrav 		if(verb) printf("success: the anchor is ok\n");
2062*b7579f77SDag-Erling Smørgrav 		ub_resolve_free(dnskey);
2063*b7579f77SDag-Erling Smørgrav 		return used_builtin;
2064*b7579f77SDag-Erling Smørgrav 	}
2065*b7579f77SDag-Erling Smørgrav 	if(force && verb) printf("debug cert update forced\n");
2066*b7579f77SDag-Erling Smørgrav 
2067*b7579f77SDag-Erling Smørgrav 	/* if not (and NOERROR): check date and do certupdate */
2068*b7579f77SDag-Erling Smørgrav 	if((dnskey->rcode == 0 &&
2069*b7579f77SDag-Erling Smørgrav 		probe_date_allows_certupdate(root_anchor_file)) || force) {
2070*b7579f77SDag-Erling Smørgrav 		if(do_certupdate(root_anchor_file, root_cert_file, urlname,
2071*b7579f77SDag-Erling Smørgrav 			xmlname, p7sname, res_conf, root_hints, debugconf,
2072*b7579f77SDag-Erling Smørgrav 			ip4only, ip6only, port, dnskey))
2073*b7579f77SDag-Erling Smørgrav 			return 1;
2074*b7579f77SDag-Erling Smørgrav 		return used_builtin;
2075*b7579f77SDag-Erling Smørgrav 	}
2076*b7579f77SDag-Erling Smørgrav 	if(verb) printf("fail: the anchor is NOT ok and could not be fixed\n");
2077*b7579f77SDag-Erling Smørgrav 	ub_resolve_free(dnskey);
2078*b7579f77SDag-Erling Smørgrav 	return used_builtin;
2079*b7579f77SDag-Erling Smørgrav }
2080*b7579f77SDag-Erling Smørgrav 
2081*b7579f77SDag-Erling Smørgrav /** getopt global, in case header files fail to declare it. */
2082*b7579f77SDag-Erling Smørgrav extern int optind;
2083*b7579f77SDag-Erling Smørgrav /** getopt global, in case header files fail to declare it. */
2084*b7579f77SDag-Erling Smørgrav extern char* optarg;
2085*b7579f77SDag-Erling Smørgrav 
2086*b7579f77SDag-Erling Smørgrav /** Main routine for unbound-anchor */
2087*b7579f77SDag-Erling Smørgrav int main(int argc, char* argv[])
2088*b7579f77SDag-Erling Smørgrav {
2089*b7579f77SDag-Erling Smørgrav 	int c;
2090*b7579f77SDag-Erling Smørgrav 	char* root_anchor_file = ROOT_ANCHOR_FILE;
2091*b7579f77SDag-Erling Smørgrav 	char* root_cert_file = ROOT_CERT_FILE;
2092*b7579f77SDag-Erling Smørgrav 	char* urlname = URLNAME;
2093*b7579f77SDag-Erling Smørgrav 	char* xmlname = XMLNAME;
2094*b7579f77SDag-Erling Smørgrav 	char* p7sname = P7SNAME;
2095*b7579f77SDag-Erling Smørgrav 	char* res_conf = NULL;
2096*b7579f77SDag-Erling Smørgrav 	char* root_hints = NULL;
2097*b7579f77SDag-Erling Smørgrav 	char* debugconf = NULL;
2098*b7579f77SDag-Erling Smørgrav 	int dolist=0, ip4only=0, ip6only=0, force=0, port = HTTPS_PORT;
2099*b7579f77SDag-Erling Smørgrav 	/* parse the options */
2100*b7579f77SDag-Erling Smørgrav 	while( (c=getopt(argc, argv, "46C:FP:a:c:f:hlr:s:u:vx:")) != -1) {
2101*b7579f77SDag-Erling Smørgrav 		switch(c) {
2102*b7579f77SDag-Erling Smørgrav 		case 'l':
2103*b7579f77SDag-Erling Smørgrav 			dolist = 1;
2104*b7579f77SDag-Erling Smørgrav 			break;
2105*b7579f77SDag-Erling Smørgrav 		case '4':
2106*b7579f77SDag-Erling Smørgrav 			ip4only = 1;
2107*b7579f77SDag-Erling Smørgrav 			break;
2108*b7579f77SDag-Erling Smørgrav 		case '6':
2109*b7579f77SDag-Erling Smørgrav 			ip6only = 1;
2110*b7579f77SDag-Erling Smørgrav 			break;
2111*b7579f77SDag-Erling Smørgrav 		case 'a':
2112*b7579f77SDag-Erling Smørgrav 			root_anchor_file = optarg;
2113*b7579f77SDag-Erling Smørgrav 			break;
2114*b7579f77SDag-Erling Smørgrav 		case 'c':
2115*b7579f77SDag-Erling Smørgrav 			root_cert_file = optarg;
2116*b7579f77SDag-Erling Smørgrav 			break;
2117*b7579f77SDag-Erling Smørgrav 		case 'u':
2118*b7579f77SDag-Erling Smørgrav 			urlname = optarg;
2119*b7579f77SDag-Erling Smørgrav 			break;
2120*b7579f77SDag-Erling Smørgrav 		case 'x':
2121*b7579f77SDag-Erling Smørgrav 			xmlname = optarg;
2122*b7579f77SDag-Erling Smørgrav 			break;
2123*b7579f77SDag-Erling Smørgrav 		case 's':
2124*b7579f77SDag-Erling Smørgrav 			p7sname = optarg;
2125*b7579f77SDag-Erling Smørgrav 			break;
2126*b7579f77SDag-Erling Smørgrav 		case 'f':
2127*b7579f77SDag-Erling Smørgrav 			res_conf = optarg;
2128*b7579f77SDag-Erling Smørgrav 			break;
2129*b7579f77SDag-Erling Smørgrav 		case 'r':
2130*b7579f77SDag-Erling Smørgrav 			root_hints = optarg;
2131*b7579f77SDag-Erling Smørgrav 			break;
2132*b7579f77SDag-Erling Smørgrav 		case 'C':
2133*b7579f77SDag-Erling Smørgrav 			debugconf = optarg;
2134*b7579f77SDag-Erling Smørgrav 			break;
2135*b7579f77SDag-Erling Smørgrav 		case 'F':
2136*b7579f77SDag-Erling Smørgrav 			force = 1;
2137*b7579f77SDag-Erling Smørgrav 			break;
2138*b7579f77SDag-Erling Smørgrav 		case 'P':
2139*b7579f77SDag-Erling Smørgrav 			port = atoi(optarg);
2140*b7579f77SDag-Erling Smørgrav 			break;
2141*b7579f77SDag-Erling Smørgrav 		case 'v':
2142*b7579f77SDag-Erling Smørgrav 			verb++;
2143*b7579f77SDag-Erling Smørgrav 			break;
2144*b7579f77SDag-Erling Smørgrav 		case '?':
2145*b7579f77SDag-Erling Smørgrav 		case 'h':
2146*b7579f77SDag-Erling Smørgrav 		default:
2147*b7579f77SDag-Erling Smørgrav 			usage();
2148*b7579f77SDag-Erling Smørgrav 		}
2149*b7579f77SDag-Erling Smørgrav 	}
2150*b7579f77SDag-Erling Smørgrav 	argc -= optind;
2151*b7579f77SDag-Erling Smørgrav 	argv += optind;
2152*b7579f77SDag-Erling Smørgrav 	if(argc != 0)
2153*b7579f77SDag-Erling Smørgrav 		usage();
2154*b7579f77SDag-Erling Smørgrav 
2155*b7579f77SDag-Erling Smørgrav 	ERR_load_crypto_strings();
2156*b7579f77SDag-Erling Smørgrav 	ERR_load_SSL_strings();
2157*b7579f77SDag-Erling Smørgrav 	OpenSSL_add_all_algorithms();
2158*b7579f77SDag-Erling Smørgrav 	(void)SSL_library_init();
2159*b7579f77SDag-Erling Smørgrav 
2160*b7579f77SDag-Erling Smørgrav 	if(dolist) do_list_builtin();
2161*b7579f77SDag-Erling Smørgrav 
2162*b7579f77SDag-Erling Smørgrav 	return do_root_update_work(root_anchor_file, root_cert_file, urlname,
2163*b7579f77SDag-Erling Smørgrav 		xmlname, p7sname, res_conf, root_hints, debugconf, ip4only,
2164*b7579f77SDag-Erling Smørgrav 		ip6only, force, port);
2165*b7579f77SDag-Erling Smørgrav }
2166