1/*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Pawe�� Dziepak, pdziepak@quarnos.org
7 */
8
9
10#include <net/dns_resolver.h>
11
12#include <AutoDeleter.h>
13#include <FindDirectory.h>
14#include <lock.h>
15#include <port.h>
16#include <team.h>
17#include <util/AutoLock.h>
18
19#include "Definitions.h"
20
21
22static 	mutex		gPortLock;
23static	port_id		gPortRequest		= -1;
24static	port_id		gPortReply			= -1;
25
26static	const int32	kQueueLength		= 1;
27
28
29static status_t
30dns_resolver_repair()
31{
32	status_t result = B_OK;
33
34	gPortRequest = create_port(kQueueLength, kPortNameReq);
35	if (gPortRequest < B_OK)
36		return gPortRequest;
37
38	gPortReply = create_port(kQueueLength, kPortNameRpl);
39	if (gPortReply < B_OK) {
40		delete_port(gPortRequest);
41		return gPortReply;
42	}
43
44	char path[256];
45	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
46		false, path, sizeof(path)) != B_OK) {
47		delete_port(gPortReply);
48		delete_port(gPortRequest);
49		return B_NAME_NOT_FOUND;
50	}
51	strlcat(path, "/dns_resolver_server", sizeof(path));
52
53	const char* args[] = { path, NULL };
54	thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
55		B_SYSTEM_TEAM, 0);
56	if (thread < B_OK) {
57		delete_port(gPortReply);
58		delete_port(gPortRequest);
59		return thread;
60	}
61
62	set_port_owner(gPortRequest, thread);
63	set_port_owner(gPortReply, thread);
64
65	result = resume_thread(thread);
66	if (result != B_OK) {
67		kill_thread(thread);
68		delete_port(gPortReply);
69		delete_port(gPortRequest);
70		return result;
71	}
72
73	return B_OK;
74}
75
76
77static status_t
78dns_resolver_init()
79{
80	mutex_init(&gPortLock, NULL);
81	return dns_resolver_repair();
82}
83
84
85static status_t
86dns_resolver_uninit()
87{
88	delete_port(gPortRequest);
89	delete_port(gPortReply);
90	mutex_destroy(&gPortLock);
91
92	return B_OK;
93}
94
95
96static void
97RelocateEntries(struct addrinfo* addr)
98{
99	char* generalOffset = reinterpret_cast<char*>(addr);
100
101	struct addrinfo* current = addr;
102	while (current != NULL) {
103		uint64 addrOffset = reinterpret_cast<uint64>(current->ai_addr);
104		uint64 nameOffset = reinterpret_cast<uint64>(current->ai_canonname);
105		uint64 nextOffset = reinterpret_cast<uint64>(current->ai_next);
106
107		if (current->ai_addr != NULL) {
108			current->ai_addr
109				= reinterpret_cast<sockaddr*>(generalOffset + addrOffset);
110		}
111
112		if (current->ai_canonname != NULL)
113			current->ai_canonname = generalOffset + nameOffset;
114
115		if (current->ai_next != NULL) {
116			current->ai_next
117				= reinterpret_cast<addrinfo*>(generalOffset + nextOffset);
118		}
119
120		current = current->ai_next;
121	}
122}
123
124
125static status_t
126GetAddrInfo(const char* node, const char* service,
127	const struct addrinfo* hints, struct addrinfo** res)
128{
129	uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1;
130	uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1;
131	uint32 size = nodeSize + serviceSize + sizeof(*hints);
132	char* buffer = reinterpret_cast<char*>(malloc(size));
133	if (buffer == NULL)
134		return B_NO_MEMORY;
135	MemoryDeleter _(buffer);
136
137	off_t off = 0;
138	if (node != NULL)
139		strcpy(buffer + off, node);
140	else
141		buffer[off] = '\0';
142	off += nodeSize;
143
144	if (service != NULL)
145		strcpy(buffer + off, service);
146	else
147		buffer[off] = '\0';
148	off += serviceSize;
149
150	if (hints != NULL)
151		memcpy(buffer + off, hints, sizeof(*hints));
152	else {
153		struct addrinfo *nullHints
154			= reinterpret_cast<struct addrinfo*>(buffer + off);
155		memset(nullHints, 0, sizeof(*nullHints));
156		nullHints->ai_family = AF_UNSPEC;
157	}
158
159	MutexLocker locker(gPortLock);
160	do {
161		status_t result = write_port(gPortRequest, MsgGetAddrInfo, buffer,
162			size);
163		if (result != B_OK) {
164			result = dns_resolver_repair();
165			if (result != B_OK)
166				return result;
167			continue;
168		}
169
170		ssize_t replySize = port_buffer_size(gPortReply);
171		if (replySize < B_OK) {
172			result = dns_resolver_repair();
173			if (result != B_OK)
174				return result;
175			continue;
176		}
177
178		void* reply = malloc(replySize);
179		if (reply == NULL)
180			return B_NO_MEMORY;
181
182		int32 code;
183		replySize = read_port(gPortReply, &code, reply, replySize);
184		if (replySize < B_OK) {
185			free(reply);
186			result = dns_resolver_repair();
187			if (result != B_OK)
188				return result;
189			continue;
190		}
191
192		struct addrinfo* addr;
193		switch (code) {
194			case MsgReply:
195				addr = reinterpret_cast<struct addrinfo*>(reply);
196				RelocateEntries(addr);
197				*res = addr;
198				return B_OK;
199			case MsgError:
200				result = *reinterpret_cast<status_t*>(reply);
201				free(reply);
202				return result;
203			default:
204				free(reply);
205				return B_BAD_VALUE;
206		}
207
208	} while (true);
209}
210
211
212static status_t
213dns_resolver_std_ops(int32 op, ...)
214{
215	switch (op) {
216		case B_MODULE_INIT:
217			return dns_resolver_init();
218		case B_MODULE_UNINIT:
219			return dns_resolver_uninit();
220		default:
221			return B_ERROR;
222	}
223}
224
225
226static dns_resolver_module sDNSResolverModule = {
227	{
228		DNS_RESOLVER_MODULE_NAME,
229		0,
230		dns_resolver_std_ops,
231	},
232
233	GetAddrInfo,
234};
235
236module_info* modules[] = {
237	(module_info*)&sDNSResolverModule,
238	NULL
239};
240
241