xref: /haiku/src/add-ons/kernel/network/dns_resolver/kernel_add_on/dns_resolver.cpp (revision 97f11716bfaa0f385eb0e28a52bf56a5023b9e99)
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 
22 static 	mutex		gPortLock;
23 static	port_id		gPortRequest		= -1;
24 static	port_id		gPortReply			= -1;
25 
26 static	const int32	kQueueLength		= 1;
27 
28 
29 static status_t
dns_resolver_repair()30 dns_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 
77 static status_t
dns_resolver_init()78 dns_resolver_init()
79 {
80 	mutex_init(&gPortLock, NULL);
81 	return dns_resolver_repair();
82 }
83 
84 
85 static status_t
dns_resolver_uninit()86 dns_resolver_uninit()
87 {
88 	delete_port(gPortRequest);
89 	delete_port(gPortReply);
90 	mutex_destroy(&gPortLock);
91 
92 	return B_OK;
93 }
94 
95 
96 static void
RelocateEntries(struct addrinfo * addr)97 RelocateEntries(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 
125 static status_t
GetAddrInfo(const char * node,const char * service,const struct addrinfo * hints,struct addrinfo ** res)126 GetAddrInfo(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 
212 static status_t
dns_resolver_std_ops(int32 op,...)213 dns_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 
226 static dns_resolver_module sDNSResolverModule = {
227 	{
228 		DNS_RESOLVER_MODULE_NAME,
229 		0,
230 		dns_resolver_std_ops,
231 	},
232 
233 	GetAddrInfo,
234 };
235 
236 module_info* modules[] = {
237 	(module_info*)&sDNSResolverModule,
238 	NULL
239 };
240 
241