fat: Greatly simplify and clean up dosfs_get_file_map().
[haiku.git] / src / preferences / mail / DNSQuery.cpp
blob0869701172da50217a2883db245d835087c511e5
1 #include "DNSQuery.h"
3 #include <errno.h>
4 #include <stdio.h>
6 #include <ByteOrder.h>
7 #include <FindDirectory.h>
8 #include <NetAddress.h>
9 #include <NetEndpoint.h>
10 #include <Path.h>
12 // #define DEBUG 1
14 #undef PRINT
15 #ifdef DEBUG
16 #define PRINT(a...) printf(a)
17 #else
18 #define PRINT(a...)
19 #endif
22 static int32 gID = 1;
25 BRawNetBuffer::BRawNetBuffer()
27 _Init(NULL, 0);
31 BRawNetBuffer::BRawNetBuffer(off_t size)
33 _Init(NULL, 0);
34 fBuffer.SetSize(size);
38 BRawNetBuffer::BRawNetBuffer(const void* buf, size_t size)
40 _Init(buf, size);
44 status_t
45 BRawNetBuffer::AppendUint16(uint16 value)
47 uint16 netVal = B_HOST_TO_BENDIAN_INT16(value);
48 ssize_t sizeW = fBuffer.WriteAt(fWritePosition, &netVal, sizeof(uint16));
49 if (sizeW == B_NO_MEMORY)
50 return B_NO_MEMORY;
51 fWritePosition += sizeof(uint16);
52 return B_OK;
56 status_t
57 BRawNetBuffer::AppendString(const char* string)
59 size_t length = strlen(string) + 1;
60 ssize_t sizeW = fBuffer.WriteAt(fWritePosition, string, length);
61 if (sizeW == B_NO_MEMORY)
62 return B_NO_MEMORY;
63 fWritePosition += length;
64 return B_OK;
68 status_t
69 BRawNetBuffer::ReadUint16(uint16& value)
71 uint16 netVal;
72 ssize_t sizeW = fBuffer.ReadAt(fReadPosition, &netVal, sizeof(uint16));
73 if (sizeW == 0)
74 return B_ERROR;
75 value= B_BENDIAN_TO_HOST_INT16(netVal);
76 fReadPosition += sizeof(uint16);
77 return B_OK;
81 status_t
82 BRawNetBuffer::ReadUint32(uint32& value)
84 uint32 netVal;
85 ssize_t sizeW = fBuffer.ReadAt(fReadPosition, &netVal, sizeof(uint32));
86 if (sizeW == 0)
87 return B_ERROR;
88 value= B_BENDIAN_TO_HOST_INT32(netVal);
89 fReadPosition += sizeof(uint32);
90 return B_OK;
94 status_t
95 BRawNetBuffer::ReadString(BString& string)
97 string = "";
98 ssize_t bytesRead = _ReadStringAt(string, fReadPosition);
99 if (bytesRead < 0)
100 return B_ERROR;
101 fReadPosition += bytesRead;
102 return B_OK;
106 status_t
107 BRawNetBuffer::SkipReading(off_t skip)
109 if (fReadPosition + skip > (off_t)fBuffer.BufferLength())
110 return B_ERROR;
111 fReadPosition += skip;
112 return B_OK;
116 void
117 BRawNetBuffer::_Init(const void* buf, size_t size)
119 fWritePosition = 0;
120 fReadPosition = 0;
121 fBuffer.WriteAt(fWritePosition, buf, size);
125 ssize_t
126 BRawNetBuffer::_ReadStringAt(BString& string, off_t pos)
128 if (pos >= (off_t)fBuffer.BufferLength())
129 return -1;
131 ssize_t bytesRead = 0;
132 char* buffer = (char*)fBuffer.Buffer();
133 buffer = &buffer[pos];
134 // if the string is compressed we have to follow the links to the
135 // sub strings
136 while (pos < (off_t)fBuffer.BufferLength() && *buffer != 0) {
137 if (uint8(*buffer) == 192) {
138 // found a pointer mark
139 buffer++;
140 bytesRead++;
141 off_t subPos = uint8(*buffer);
142 _ReadStringAt(string, subPos);
143 break;
145 string.Append(buffer, 1);
146 buffer++;
147 bytesRead++;
149 bytesRead++;
150 return bytesRead;
154 // #pragma mark - DNSTools
157 status_t
158 DNSTools::GetDNSServers(BObjectList<BString>* serverList)
160 // TODO: reading resolv.conf ourselves shouldn't be needed.
161 // we should have some function to retrieve the dns list
162 #define MATCH(line, name) \
163 (!strncmp(line, name, sizeof(name) - 1) && \
164 (line[sizeof(name) - 1] == ' ' || \
165 line[sizeof(name) - 1] == '\t'))
167 BPath path;
168 if (find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path) != B_OK)
169 return B_ENTRY_NOT_FOUND;
171 path.Append("network/resolv.conf");
173 register FILE* fp = fopen(path.Path(), "r");
174 if (fp == NULL) {
175 fprintf(stderr, "failed to open '%s' to read nameservers: %s\n",
176 path.Path(), strerror(errno));
177 return B_ENTRY_NOT_FOUND;
180 int nserv = 0;
181 char buf[1024];
182 register char *cp; //, **pp;
183 // register int n;
184 int MAXNS = 2;
186 // read the config file
187 while (fgets(buf, sizeof(buf), fp) != NULL) {
188 // skip comments
189 if (*buf == ';' || *buf == '#')
190 continue;
192 // read nameservers to query
193 if (MATCH(buf, "nameserver") && nserv < MAXNS) {
194 // char sbuf[2];
195 cp = buf + sizeof("nameserver") - 1;
196 while (*cp == ' ' || *cp == '\t')
197 cp++;
198 cp[strcspn(cp, ";# \t\n")] = '\0';
199 if ((*cp != '\0') && (*cp != '\n')) {
200 serverList->AddItem(new BString(cp));
201 nserv++;
204 continue;
207 fclose(fp);
209 return B_OK;
213 BString
214 DNSTools::ConvertToDNSName(const BString& string)
216 BString outString = string;
217 int32 dot, lastDot, diff;
219 dot = string.FindFirst(".");
220 if (dot != B_ERROR) {
221 outString.Prepend((char*)&dot, 1);
222 // because we prepend a char add 1 more
223 lastDot = dot + 1;
225 while (true) {
226 dot = outString.FindFirst(".", lastDot + 1);
227 if (dot == B_ERROR)
228 break;
230 // set a counts to the dot
231 diff = dot - 1 - lastDot;
232 outString[lastDot] = (char)diff;
233 lastDot = dot;
235 } else
236 lastDot = 0;
238 diff = outString.CountChars() - 1 - lastDot;
239 outString[lastDot] = (char)diff;
241 return outString;
245 BString
246 DNSTools::ConvertFromDNSName(const BString& string)
248 if (string.Length() == 0)
249 return string;
251 BString outString = string;
252 int32 dot = string[0];
253 int32 nextDot = dot;
254 outString.Remove(0, sizeof(char));
255 while (true) {
256 if (nextDot >= outString.Length())
257 break;
258 dot = outString[nextDot];
259 if (dot == 0)
260 break;
261 // set a "."
262 outString[nextDot] = '.';
263 nextDot+= dot + 1;
265 return outString;
269 // #pragma mark - DNSQuery
270 // see http://tools.ietf.org/html/rfc1035 for more information about DNS
273 DNSQuery::DNSQuery()
278 DNSQuery::~DNSQuery()
283 status_t
284 DNSQuery::ReadDNSServer(in_addr* add)
286 // list owns the items
287 BObjectList<BString> dnsServerList(5, true);
288 status_t status = DNSTools::GetDNSServers(&dnsServerList);
289 if (status != B_OK)
290 return status;
292 BString* firstDNS = dnsServerList.ItemAt(0);
293 if (firstDNS == NULL || inet_aton(firstDNS->String(), add) != 1)
294 return B_ERROR;
296 PRINT("dns server found: %s \n", firstDNS->String());
297 return B_OK;
301 status_t
302 DNSQuery::GetMXRecords(const BString& serverName,
303 BObjectList<mx_record>* mxList, bigtime_t timeout)
305 // get the DNS server to ask for the mx record
306 in_addr dnsAddress;
307 if (ReadDNSServer(&dnsAddress) != B_OK)
308 return B_ERROR;
310 // create dns query package
311 BRawNetBuffer buffer;
312 dns_header header;
313 _SetMXHeader(&header);
314 _AppendQueryHeader(buffer, &header);
316 BString serverNameConv = DNSTools::ConvertToDNSName(serverName);
317 buffer.AppendString(serverNameConv);
318 buffer.AppendUint16(uint16(MX_RECORD));
319 buffer.AppendUint16(uint16(1));
321 // send the buffer
322 PRINT("send buffer\n");
323 BNetAddress netAddress(dnsAddress, 53);
324 BNetEndpoint netEndpoint(SOCK_DGRAM);
325 if (netEndpoint.InitCheck() != B_OK)
326 return B_ERROR;
328 if (netEndpoint.Connect(netAddress) != B_OK)
329 return B_ERROR;
330 PRINT("Connected\n");
332 int32 bytesSend = netEndpoint.Send(buffer.Data(), buffer.Size());
333 if (bytesSend == B_ERROR)
334 return B_ERROR;
335 PRINT("bytes send %i\n", int(bytesSend));
337 // receive buffer
338 BRawNetBuffer receiBuffer(512);
339 netEndpoint.SetTimeout(timeout);
341 int32 bytesRecei = netEndpoint.ReceiveFrom(receiBuffer.Data(), 512,
342 netAddress);
343 if (bytesRecei == B_ERROR)
344 return B_ERROR;
345 PRINT("bytes received %i\n", int(bytesRecei));
347 dns_header receiHeader;
349 _ReadQueryHeader(receiBuffer, &receiHeader);
350 PRINT("Package contains :");
351 PRINT("%d Questions, ", receiHeader.q_count);
352 PRINT("%d Answers, ", receiHeader.ans_count);
353 PRINT("%d Authoritative Servers, ", receiHeader.auth_count);
354 PRINT("%d Additional records\n", receiHeader.add_count);
356 // remove name and Question
357 BString dummyS;
358 uint16 dummy;
359 receiBuffer.ReadString(dummyS);
360 receiBuffer.ReadUint16(dummy);
361 receiBuffer.ReadUint16(dummy);
363 bool mxRecordFound = false;
364 for (int i = 0; i < receiHeader.ans_count; i++) {
365 resource_record_head rrHead;
366 _ReadResourceRecord(receiBuffer, &rrHead);
367 if (rrHead.type == MX_RECORD) {
368 mx_record* mxRec = new mx_record;
369 _ReadMXRecord(receiBuffer, mxRec);
370 PRINT("MX record found pri %i, name %s\n",
371 mxRec->priority, mxRec->serverName.String());
372 // Add mx record to the list
373 mxList->AddItem(mxRec);
374 mxRecordFound = true;
375 } else {
376 buffer.SkipReading(rrHead.dataLength);
380 if (!mxRecordFound)
381 return B_ERROR;
383 return B_OK;
387 uint16
388 DNSQuery::_GetUniqueID()
390 int32 nextId= atomic_add(&gID, 1);
391 // just to be sure
392 if (nextId > 65529)
393 nextId = 0;
394 return nextId;
398 void
399 DNSQuery::_SetMXHeader(dns_header* header)
401 header->id = _GetUniqueID();
402 header->qr = 0; //This is a query
403 header->opcode = 0; //This is a standard query
404 header->aa = 0; //Not Authoritative
405 header->tc = 0; //This message is not truncated
406 header->rd = 1; //Recursion Desired
407 header->ra = 0; //Recursion not available! hey we dont have it (lol)
408 header->z = 0;
409 header->rcode = 0;
410 header->q_count = 1; //we have only 1 question
411 header->ans_count = 0;
412 header->auth_count = 0;
413 header->add_count = 0;
417 void
418 DNSQuery::_AppendQueryHeader(BRawNetBuffer& buffer, const dns_header* header)
420 buffer.AppendUint16(header->id);
421 uint16 data = 0;
422 data |= header->rcode;
423 data |= header->z << 4;
424 data |= header->ra << 7;
425 data |= header->rd << 8;
426 data |= header->tc << 9;
427 data |= header->aa << 10;
428 data |= header->opcode << 11;
429 data |= header->qr << 15;
430 buffer.AppendUint16(data);
431 buffer.AppendUint16(header->q_count);
432 buffer.AppendUint16(header->ans_count);
433 buffer.AppendUint16(header->auth_count);
434 buffer.AppendUint16(header->add_count);
438 void
439 DNSQuery::_ReadQueryHeader(BRawNetBuffer& buffer, dns_header* header)
441 buffer.ReadUint16(header->id);
442 uint16 data = 0;
443 buffer.ReadUint16(data);
444 header->rcode = data & 0x0F;
445 header->z = (data >> 4) & 0x07;
446 header->ra = (data >> 7) & 0x01;
447 header->rd = (data >> 8) & 0x01;
448 header->tc = (data >> 9) & 0x01;
449 header->aa = (data >> 10) & 0x01;
450 header->opcode = (data >> 11) & 0x0F;
451 header->qr = (data >> 15) & 0x01;
452 buffer.ReadUint16(header->q_count);
453 buffer.ReadUint16(header->ans_count);
454 buffer.ReadUint16(header->auth_count);
455 buffer.ReadUint16(header->add_count);
459 void
460 DNSQuery::_ReadMXRecord(BRawNetBuffer& buffer, mx_record* mxRecord)
462 buffer.ReadUint16(mxRecord->priority);
463 buffer.ReadString(mxRecord->serverName);
464 mxRecord->serverName = DNSTools::ConvertFromDNSName(mxRecord->serverName);
468 void
469 DNSQuery::_ReadResourceRecord(BRawNetBuffer& buffer,
470 resource_record_head *rrHead)
472 buffer.ReadString(rrHead->name);
473 buffer.ReadUint16(rrHead->type);
474 buffer.ReadUint16(rrHead->dataClass);
475 buffer.ReadUint32(rrHead->ttl);
476 buffer.ReadUint16(rrHead->dataLength);