1/*-
2 * Copyright (c) 2010 Alistair Crooks <agc@NetBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include <sys/types.h>
26#include <sys/param.h>
27#include <sys/socket.h>
28
29#include <netinet/in.h>
30
31#include <errno.h>
32#include <inttypes.h>
33#include <netdb.h>
34#include <netpgp.h>
35#include <regex.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include "hkpc.h"
42
43/* get a socket and connect it to the server */
44int
45hkpc_connect(const char *hostname, const int port, const int fam)
46{
47        struct addrinfo  hints;
48        struct addrinfo *res;
49        char             portstr[32];
50	int		 sock;
51        int              rc = 0;
52
53        (void) memset(&hints, 0, sizeof(hints));
54        hints.ai_family = (fam == 4) ? PF_INET : PF_INET6;
55        hints.ai_socktype = SOCK_STREAM;
56        (void) snprintf(portstr, sizeof(portstr), "%d", port);
57        if ((rc = getaddrinfo(hostname, portstr, &hints, &res)) != 0) {
58                hints.ai_flags = 0;
59                if ((rc = getaddrinfo(hostname, "hkp", &hints, &res)) != 0) {
60                        (void) fprintf(stderr, "getaddrinfo: %s",
61					gai_strerror(rc));
62                        return -1;
63                }
64        }
65	if ((sock = socket((fam == 4) ? AF_INET : AF_INET6, SOCK_STREAM, 0)) < 0) {
66                (void) fprintf(stderr, "socket failed %d\n", errno);
67                freeaddrinfo(res);
68                return -1;
69	}
70        if ((rc = connect(sock, res->ai_addr, res->ai_addrlen)) < 0) {
71                (void) fprintf(stderr, "connect failed %d\n", errno);
72                freeaddrinfo(res);
73                return -1;
74        }
75        freeaddrinfo(res);
76        if (rc < 0) {
77                (void) fprintf(stderr, "connect() to %s:%d failed (rc %d)\n",
78				hostname, port, rc);
79        }
80        return sock;
81}
82
83#define MB(x)	((x) * 1024 * 1024)
84
85/* get required info from the server */
86int
87hkpc_get(char **info, const char *server, const int port, const int family, const char *type, const char *userid)
88{
89	char	buf[MB(1)];
90	int	sock;
91	int	cc;
92	int	rc;
93
94	if ((sock = hkpc_connect(server, port, family)) < 0) {
95		(void) fprintf(stderr, "hkpc_get: can't connect to server '%s'\n", server);
96		return -1;
97	}
98	cc = snprintf(buf, sizeof(buf), "GET /pks/lookup?op=%s&search=%s&options=json", type, userid);
99	if (write(sock, buf, cc) != cc) {
100		(void) fprintf(stderr, "hkpc_get: short write\n");
101		return -1;
102	}
103	for (cc = 0 ; (rc = read(sock, &buf[cc], sizeof(buf) - cc)) > 0 ; cc += rc) {
104	}
105	*info = calloc(1, cc + 1);
106	(void) memcpy(*info, buf, cc);
107	(*info)[cc] = 0x0;
108	(void) close(sock);
109	return cc;
110}
111
112/* jump over http header, then pass the json to the key-formatting function */
113int
114hkpc_print_key(FILE *fp, const char *op, const char *res)
115{
116	static regex_t	text;
117	static int	compiled;
118	regmatch_t	matches[10];
119	int	 	ret;
120
121	if (!compiled) {
122		compiled = 1;
123		(void) regcomp(&text, "\r\n\r\n", REG_EXTENDED);
124	}
125	if (regexec(&text, res, 10, matches, 0) != 0) {
126		return 0;
127	}
128	if (strcmp(op, "index") == 0 || strcmp(op, "vindex") == 0) {
129		ret = netpgp_format_json(fp, &res[(int)matches[0].rm_eo], 1);
130	} else {
131		(void) fprintf(fp, "%s\n", &res[(int)matches[0].rm_eo]);
132		ret = 1;
133	}
134	return ret;
135}
136