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