1 /* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "istream.h"
5 #include "ostream.h"
6 #include "array.h"
7 #include "strfuncs.h"
8 #include "connection.h"
9 #include "restrict-access.h"
10 #include "master-service.h"
11 
12 #include <unistd.h>
13 
14 static struct event_category event_category_dns = {
15 	.name = "dns-worker"
16 };
17 
18 static struct connection_list *dns_clients = NULL;
19 
dns_client_input_args(struct connection * client,const char * const * args)20 static int dns_client_input_args(struct connection *client, const char *const *args)
21 {
22 	struct ip_addr *ips, ip;
23 	const char *name;
24 	struct event *event;
25 	unsigned int i, ips_count;
26 	int ret;
27 	struct event_passthrough *e;
28 
29 	if (strcmp(args[0], "QUIT") == 0) {
30 		return -1;
31 	} else if (args[1] == NULL) {
32 		e_error(client->event, "Got empty request");
33 		return -1;
34 	}
35 
36 	event = event_create(client->event);
37 	event_set_append_log_prefix(event, t_strconcat(args[1], ": ", NULL));
38 
39 	e = event_create_passthrough(event)->
40                 set_name("dns_worker_request_started")->
41                 add_str("name", args[1]);
42 	e_debug(e->event(), "Resolving");
43 
44 	e = event_create_passthrough(event)->
45 		set_name("dns_worker_request_finished")->
46 		add_str("name", args[1]);
47 
48 	if (strcmp(args[0], "IP") == 0) {
49 		ret = net_gethostbyname(args[1], &ips, &ips_count);
50 		if (ret == 0 && ips_count == 0) {
51 			/* shouldn't happen, but fix it anyway.. */
52 			ret = EAI_NONAME;
53 		}
54 		/* update timestamp after hostname lookup so the event duration
55 		   field gets set correctly */
56 		io_loop_time_refresh();
57 		if (ret != 0) {
58 			const char *err = net_gethosterror(ret);
59 			e->add_int("error_code", ret);
60 			e->add_str("error", err);
61 			e_debug(e->event(), "Resolve failed: %s", err);
62 			o_stream_nsend_str(client->output,
63 				t_strdup_printf("%d\t%s\n", ret, err));
64 		} else {
65 			ARRAY_TYPE(const_string) tmp;
66 			t_array_init(&tmp, ips_count);
67 			o_stream_nsend_str(client->output, "0\t");
68 			for (i = 0; i < ips_count; i++) {
69 				const char *ip = net_ip2addr(&ips[i]);
70 				array_push_back(&tmp, &ip);
71 			}
72 			array_append_zero(&tmp);
73 			e_debug(e->event(), "Resolve success: %s",
74 				t_strarray_join(array_front(&tmp), ", "));
75 			o_stream_nsend_str(client->output,
76 					   t_strarray_join(array_front(&tmp), "\t"));
77 			o_stream_nsend_str(client->output, "\n");
78 		}
79 	} else if (strcmp(args[0], "NAME") == 0) {
80 		if (net_addr2ip(args[1], &ip) < 0) {
81 			e->add_int("error_code", EAI_FAIL);
82 			e->add_str("error", "Not an IP");
83 			e_debug(e->event(), "Resolve failed: Not an IP");
84 			o_stream_nsend_str(client->output, "-1\tNot an IP\n");
85 		} else if ((ret = net_gethostbyaddr(&ip, &name)) != 0) {
86 			const char *err = net_gethosterror(ret);
87 			e->add_int("error_code", ret);
88 			e->add_str("error", err);
89 			e_debug(e->event(), "Resolve failed: %s", err);
90 			o_stream_nsend_str(client->output,
91 				t_strdup_printf("%d\t%s\n", ret, err));
92 		} else {
93 			e_debug(e->event(), "Resolve success: %s", name);
94 			o_stream_nsend_str(client->output,
95 				t_strdup_printf("0\t%s\n", name));
96 		}
97 	} else {
98 		e->add_str("error", "Unknown command");
99 		e_error(e->event(), "Unknown command '%s'", args[0]);
100 		o_stream_nsend_str(client->output, "-1\tUnknown command\n");
101 	}
102 
103 	event_unref(&event);
104 
105 	return 1;
106 }
107 
dns_client_destroy(struct connection * client)108 static void dns_client_destroy(struct connection *client)
109 {
110 	connection_deinit(client);
111 	event_unref(&client->event);
112 	i_free(client);
113 	master_service_client_connection_destroyed(master_service);
114 }
115 
116 static const struct connection_vfuncs dns_client_vfuncs = {
117 	.input_args = dns_client_input_args,
118 	.destroy = dns_client_destroy
119 };
120 
121 static const struct connection_settings dns_client_set = {
122 	.service_name_in = "dns-client",
123 	.service_name_out = "dns",
124 	.major_version = 1,
125 	.minor_version = 0,
126 	.input_max_size = SIZE_MAX,
127 	.output_max_size = SIZE_MAX
128 };
129 
client_connected(struct master_service_connection * master_conn)130 static void client_connected(struct master_service_connection *master_conn)
131 {
132 	struct connection *conn = i_new(struct connection, 1);
133 	master_service_client_connection_accept(master_conn);
134 	connection_init_server(dns_clients, conn, master_conn->name,
135 			       master_conn->fd, master_conn->fd);
136 	event_add_category(conn->event, &event_category_dns);
137 }
138 
main(int argc,char * argv[])139 int main(int argc, char *argv[])
140 {
141 	master_service = master_service_init("dns-client", 0,
142 					     &argc, &argv, "");
143 	if (master_getopt(master_service) > 0)
144 		return FATAL_DEFAULT;
145 
146 	master_service_init_log(master_service);
147 	restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL);
148 	restrict_access_allow_coredumps(TRUE);
149 
150 	/* setup connection list */
151 	dns_clients = connection_list_init(&dns_client_set, &dns_client_vfuncs);
152 
153 	master_service_init_finish(master_service);
154 	master_service_run(master_service, client_connected);
155 
156 	/* disconnect all clients */
157 	connection_list_deinit(&dns_clients);
158 
159 	master_service_deinit(&master_service);
160         return 0;
161 }
162