1 #include "DNSQuery.h" 2 3 #include <errno.h> 4 #include <stdio.h> 5 6 #include <ByteOrder.h> 7 #include <FindDirectory.h> 8 #include <NetAddress.h> 9 #include <NetEndpoint.h> 10 #include <Path.h> 11 12 // #define DEBUG 1 13 14 #undef PRINT 15 #ifdef DEBUG 16 #define PRINT(a...) printf(a) 17 #else 18 #define PRINT(a...) 19 #endif 20 21 22 static int32 gID = 1; 23 24 25 BRawNetBuffer::BRawNetBuffer() 26 { 27 _Init(NULL, 0); 28 } 29 30 31 BRawNetBuffer::BRawNetBuffer(off_t size) 32 { 33 _Init(NULL, 0); 34 fBuffer.SetSize(size); 35 } 36 37 38 BRawNetBuffer::BRawNetBuffer(const void* buf, size_t size) 39 { 40 _Init(buf, size); 41 } 42 43 44 status_t 45 BRawNetBuffer::AppendUint16(uint16 value) 46 { 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; 53 } 54 55 56 status_t 57 BRawNetBuffer::AppendString(const char* string) 58 { 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; 65 } 66 67 68 status_t 69 BRawNetBuffer::ReadUint16(uint16& value) 70 { 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; 78 } 79 80 81 status_t 82 BRawNetBuffer::ReadUint32(uint32& value) 83 { 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; 91 } 92 93 94 status_t 95 BRawNetBuffer::ReadString(BString& string) 96 { 97 string = ""; 98 ssize_t bytesRead = _ReadStringAt(string, fReadPosition); 99 if (bytesRead < 0) 100 return B_ERROR; 101 fReadPosition += bytesRead; 102 return B_OK; 103 } 104 105 106 status_t 107 BRawNetBuffer::SkipReading(off_t skip) 108 { 109 if (fReadPosition + skip > (off_t)fBuffer.BufferLength()) 110 return B_ERROR; 111 fReadPosition += skip; 112 return B_OK; 113 } 114 115 116 void 117 BRawNetBuffer::_Init(const void* buf, size_t size) 118 { 119 fWritePosition = 0; 120 fReadPosition = 0; 121 fBuffer.WriteAt(fWritePosition, buf, size); 122 } 123 124 125 ssize_t 126 BRawNetBuffer::_ReadStringAt(BString& string, off_t pos) 127 { 128 if (pos >= (off_t)fBuffer.BufferLength()) 129 return -1; 130 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; 144 } 145 string.Append(buffer, 1); 146 buffer++; 147 bytesRead++; 148 } 149 bytesRead++; 150 return bytesRead; 151 } 152 153 154 // #pragma mark - DNSTools 155 156 157 status_t 158 DNSTools::GetDNSServers(BObjectList<BString>* serverList) 159 { 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')) 166 167 BPath path; 168 if (find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path) != B_OK) 169 return B_ENTRY_NOT_FOUND; 170 171 path.Append("network/resolv.conf"); 172 173 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; 178 } 179 180 int nserv = 0; 181 char buf[1024]; 182 char *cp; //, **pp; 183 int MAXNS = 2; 184 185 // read the config file 186 while (fgets(buf, sizeof(buf), fp) != NULL) { 187 // skip comments 188 if (*buf == ';' || *buf == '#') 189 continue; 190 191 // read nameservers to query 192 if (MATCH(buf, "nameserver") && nserv < MAXNS) { 193 // char sbuf[2]; 194 cp = buf + sizeof("nameserver") - 1; 195 while (*cp == ' ' || *cp == '\t') 196 cp++; 197 cp[strcspn(cp, ";# \t\n")] = '\0'; 198 if ((*cp != '\0') && (*cp != '\n')) { 199 serverList->AddItem(new BString(cp)); 200 nserv++; 201 } 202 } 203 continue; 204 } 205 206 fclose(fp); 207 208 return B_OK; 209 } 210 211 212 BString 213 DNSTools::ConvertToDNSName(const BString& string) 214 { 215 BString outString = string; 216 int32 dot, lastDot, diff; 217 218 dot = string.FindFirst("."); 219 if (dot != B_ERROR) { 220 outString.Prepend((char*)&dot, 1); 221 // because we prepend a char add 1 more 222 lastDot = dot + 1; 223 224 while (true) { 225 dot = outString.FindFirst(".", lastDot + 1); 226 if (dot == B_ERROR) 227 break; 228 229 // set a counts to the dot 230 diff = dot - 1 - lastDot; 231 outString.SetByteAt(lastDot, (char)diff); 232 lastDot = dot; 233 } 234 } else 235 lastDot = 0; 236 237 diff = outString.CountChars() - 1 - lastDot; 238 outString.SetByteAt(lastDot, (char)diff); 239 240 return outString; 241 } 242 243 244 BString 245 DNSTools::ConvertFromDNSName(const BString& string) 246 { 247 if (string.Length() == 0) 248 return string; 249 250 BString outString = string; 251 int32 dot = string[0]; 252 int32 nextDot = dot; 253 outString.Remove(0, sizeof(char)); 254 while (true) { 255 if (nextDot >= outString.Length()) 256 break; 257 dot = outString[nextDot]; 258 if (dot == 0) 259 break; 260 // set a "." 261 outString.SetByteAt(nextDot, '.'); 262 nextDot+= dot + 1; 263 } 264 return outString; 265 } 266 267 268 // #pragma mark - DNSQuery 269 // see http://tools.ietf.org/html/rfc1035 for more information about DNS 270 271 272 DNSQuery::DNSQuery() 273 { 274 } 275 276 277 DNSQuery::~DNSQuery() 278 { 279 } 280 281 282 status_t 283 DNSQuery::ReadDNSServer(in_addr* add) 284 { 285 // list owns the items 286 BObjectList<BString> dnsServerList(5, true); 287 status_t status = DNSTools::GetDNSServers(&dnsServerList); 288 if (status != B_OK) 289 return status; 290 291 BString* firstDNS = dnsServerList.ItemAt(0); 292 if (firstDNS == NULL || inet_aton(firstDNS->String(), add) != 1) 293 return B_ERROR; 294 295 PRINT("dns server found: %s \n", firstDNS->String()); 296 return B_OK; 297 } 298 299 300 status_t 301 DNSQuery::GetMXRecords(const BString& serverName, 302 BObjectList<mx_record>* mxList, bigtime_t timeout) 303 { 304 // get the DNS server to ask for the mx record 305 in_addr dnsAddress; 306 if (ReadDNSServer(&dnsAddress) != B_OK) 307 return B_ERROR; 308 309 // create dns query package 310 BRawNetBuffer buffer; 311 dns_header header; 312 _SetMXHeader(&header); 313 _AppendQueryHeader(buffer, &header); 314 315 BString serverNameConv = DNSTools::ConvertToDNSName(serverName); 316 buffer.AppendString(serverNameConv); 317 buffer.AppendUint16(uint16(MX_RECORD)); 318 buffer.AppendUint16(uint16(1)); 319 320 // send the buffer 321 PRINT("send buffer\n"); 322 BNetAddress netAddress(dnsAddress, 53); 323 BNetEndpoint netEndpoint(SOCK_DGRAM); 324 if (netEndpoint.InitCheck() != B_OK) 325 return B_ERROR; 326 327 if (netEndpoint.Connect(netAddress) != B_OK) 328 return B_ERROR; 329 PRINT("Connected\n"); 330 331 int32 bytesSend = netEndpoint.Send(buffer.Data(), buffer.Size()); 332 if (bytesSend == B_ERROR) 333 return B_ERROR; 334 PRINT("bytes send %i\n", int(bytesSend)); 335 336 // receive buffer 337 BRawNetBuffer receiBuffer(512); 338 netEndpoint.SetTimeout(timeout); 339 340 int32 bytesRecei = netEndpoint.ReceiveFrom(receiBuffer.Data(), 512, 341 netAddress); 342 if (bytesRecei == B_ERROR) 343 return B_ERROR; 344 PRINT("bytes received %i\n", int(bytesRecei)); 345 346 dns_header receiHeader; 347 348 _ReadQueryHeader(receiBuffer, &receiHeader); 349 PRINT("Package contains :"); 350 PRINT("%d Questions, ", receiHeader.q_count); 351 PRINT("%d Answers, ", receiHeader.ans_count); 352 PRINT("%d Authoritative Servers, ", receiHeader.auth_count); 353 PRINT("%d Additional records\n", receiHeader.add_count); 354 355 // remove name and Question 356 BString dummyS; 357 uint16 dummy; 358 receiBuffer.ReadString(dummyS); 359 receiBuffer.ReadUint16(dummy); 360 receiBuffer.ReadUint16(dummy); 361 362 bool mxRecordFound = false; 363 for (int i = 0; i < receiHeader.ans_count; i++) { 364 resource_record_head rrHead; 365 _ReadResourceRecord(receiBuffer, &rrHead); 366 if (rrHead.type == MX_RECORD) { 367 mx_record* mxRec = new mx_record; 368 _ReadMXRecord(receiBuffer, mxRec); 369 PRINT("MX record found pri %i, name %s\n", 370 mxRec->priority, mxRec->serverName.String()); 371 // Add mx record to the list 372 mxList->AddItem(mxRec); 373 mxRecordFound = true; 374 } else { 375 buffer.SkipReading(rrHead.dataLength); 376 } 377 } 378 379 if (!mxRecordFound) 380 return B_ERROR; 381 382 return B_OK; 383 } 384 385 386 uint16 387 DNSQuery::_GetUniqueID() 388 { 389 int32 nextId= atomic_add(&gID, 1); 390 // just to be sure 391 if (nextId > 65529) 392 nextId = 0; 393 return nextId; 394 } 395 396 397 void 398 DNSQuery::_SetMXHeader(dns_header* header) 399 { 400 header->id = _GetUniqueID(); 401 header->qr = 0; //This is a query 402 header->opcode = 0; //This is a standard query 403 header->aa = 0; //Not Authoritative 404 header->tc = 0; //This message is not truncated 405 header->rd = 1; //Recursion Desired 406 header->ra = 0; //Recursion not available! hey we dont have it (lol) 407 header->z = 0; 408 header->rcode = 0; 409 header->q_count = 1; //we have only 1 question 410 header->ans_count = 0; 411 header->auth_count = 0; 412 header->add_count = 0; 413 } 414 415 416 void 417 DNSQuery::_AppendQueryHeader(BRawNetBuffer& buffer, const dns_header* header) 418 { 419 buffer.AppendUint16(header->id); 420 uint16 data = 0; 421 data |= header->rcode; 422 data |= header->z << 4; 423 data |= header->ra << 7; 424 data |= header->rd << 8; 425 data |= header->tc << 9; 426 data |= header->aa << 10; 427 data |= header->opcode << 11; 428 data |= header->qr << 15; 429 buffer.AppendUint16(data); 430 buffer.AppendUint16(header->q_count); 431 buffer.AppendUint16(header->ans_count); 432 buffer.AppendUint16(header->auth_count); 433 buffer.AppendUint16(header->add_count); 434 } 435 436 437 void 438 DNSQuery::_ReadQueryHeader(BRawNetBuffer& buffer, dns_header* header) 439 { 440 buffer.ReadUint16(header->id); 441 uint16 data = 0; 442 buffer.ReadUint16(data); 443 header->rcode = data & 0x0F; 444 header->z = (data >> 4) & 0x07; 445 header->ra = (data >> 7) & 0x01; 446 header->rd = (data >> 8) & 0x01; 447 header->tc = (data >> 9) & 0x01; 448 header->aa = (data >> 10) & 0x01; 449 header->opcode = (data >> 11) & 0x0F; 450 header->qr = (data >> 15) & 0x01; 451 buffer.ReadUint16(header->q_count); 452 buffer.ReadUint16(header->ans_count); 453 buffer.ReadUint16(header->auth_count); 454 buffer.ReadUint16(header->add_count); 455 } 456 457 458 void 459 DNSQuery::_ReadMXRecord(BRawNetBuffer& buffer, mx_record* mxRecord) 460 { 461 buffer.ReadUint16(mxRecord->priority); 462 buffer.ReadString(mxRecord->serverName); 463 mxRecord->serverName = DNSTools::ConvertFromDNSName(mxRecord->serverName); 464 } 465 466 467 void 468 DNSQuery::_ReadResourceRecord(BRawNetBuffer& buffer, 469 resource_record_head *rrHead) 470 { 471 buffer.ReadString(rrHead->name); 472 buffer.ReadUint16(rrHead->type); 473 buffer.ReadUint16(rrHead->dataClass); 474 buffer.ReadUint32(rrHead->ttl); 475 buffer.ReadUint16(rrHead->dataLength); 476 } 477