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