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 vint32 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_COMMON_SETTINGS_DIRECTORY, &path) != B_OK) 169 return B_ENTRY_NOT_FOUND; 170 171 path.Append("network/resolv.conf"); 172 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; 178 } 179 180 int nserv = 0; 181 char buf[1024]; 182 register char *cp; //, **pp; 183 // register int n; 184 int MAXNS = 2; 185 186 // read the config file 187 while (fgets(buf, sizeof(buf), fp) != NULL) { 188 // skip comments 189 if (*buf == ';' || *buf == '#') 190 continue; 191 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++; 202 } 203 } 204 continue; 205 } 206 207 fclose(fp); 208 209 return B_OK; 210 } 211 212 213 BString 214 DNSTools::ConvertToDNSName(const BString& string) 215 { 216 BString outString = string; 217 int32 dot, lastDot, diff; 218 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; 224 225 while (true) { 226 dot = outString.FindFirst(".", lastDot + 1); 227 if (dot == B_ERROR) 228 break; 229 230 // set a counts to the dot 231 diff = dot - 1 - lastDot; 232 outString[lastDot] = (char)diff; 233 lastDot = dot; 234 } 235 } else 236 lastDot = 0; 237 238 diff = outString.CountChars() - 1 - lastDot; 239 outString[lastDot] = (char)diff; 240 241 return outString; 242 } 243 244 245 BString 246 DNSTools::ConvertFromDNSName(const BString& string) 247 { 248 if (string.Length() == 0) 249 return string; 250 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; 264 } 265 return outString; 266 } 267 268 269 // #pragma mark - DNSQuery 270 // see http://tools.ietf.org/html/rfc1035 for more information about DNS 271 272 273 DNSQuery::DNSQuery() 274 { 275 } 276 277 278 DNSQuery::~DNSQuery() 279 { 280 } 281 282 283 status_t 284 DNSQuery::ReadDNSServer(in_addr* add) 285 { 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; 291 292 BString* firstDNS = dnsServerList.ItemAt(0); 293 if (firstDNS == NULL || inet_aton(firstDNS->String(), add) != 1) 294 return B_ERROR; 295 296 PRINT("dns server found: %s \n", firstDNS->String()); 297 return B_OK; 298 } 299 300 301 status_t 302 DNSQuery::GetMXRecords(const BString& serverName, 303 BObjectList<mx_record>* mxList, bigtime_t timeout) 304 { 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; 309 310 // create dns query package 311 BRawNetBuffer buffer; 312 dns_header header; 313 _SetMXHeader(&header); 314 _AppendQueryHeader(buffer, &header); 315 316 BString serverNameConv = DNSTools::ConvertToDNSName(serverName); 317 buffer.AppendString(serverNameConv); 318 buffer.AppendUint16(uint16(MX_RECORD)); 319 buffer.AppendUint16(uint16(1)); 320 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; 327 328 if (netEndpoint.Connect(netAddress) != B_OK) 329 return B_ERROR; 330 PRINT("Connected\n"); 331 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)); 336 337 // receive buffer 338 BRawNetBuffer receiBuffer(512); 339 netEndpoint.SetTimeout(timeout); 340 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)); 346 347 dns_header receiHeader; 348 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); 355 356 // remove name and Question 357 BString dummyS; 358 uint16 dummy; 359 receiBuffer.ReadString(dummyS); 360 receiBuffer.ReadUint16(dummy); 361 receiBuffer.ReadUint16(dummy); 362 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); 377 } 378 } 379 380 if (!mxRecordFound) 381 return B_ERROR; 382 383 return B_OK; 384 } 385 386 387 uint16 388 DNSQuery::_GetUniqueID() 389 { 390 int32 nextId= atomic_add(&gID, 1); 391 // just to be sure 392 if (nextId > 65529) 393 nextId = 0; 394 return nextId; 395 } 396 397 398 void 399 DNSQuery::_SetMXHeader(dns_header* header) 400 { 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; 414 } 415 416 417 void 418 DNSQuery::_AppendQueryHeader(BRawNetBuffer& buffer, const dns_header* header) 419 { 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); 435 } 436 437 438 void 439 DNSQuery::_ReadQueryHeader(BRawNetBuffer& buffer, dns_header* header) 440 { 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); 456 } 457 458 459 void 460 DNSQuery::_ReadMXRecord(BRawNetBuffer& buffer, mx_record* mxRecord) 461 { 462 buffer.ReadUint16(mxRecord->priority); 463 buffer.ReadString(mxRecord->serverName); 464 mxRecord->serverName = DNSTools::ConvertFromDNSName(mxRecord->serverName); 465 } 466 467 468 void 469 DNSQuery::_ReadResourceRecord(BRawNetBuffer& buffer, 470 resource_record_head *rrHead) 471 { 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); 477 } 478