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