Fixed issue #1642: Incorrect behavior if repo is located on root of drive
[TortoiseGit.git] / src / Utils / dnsmx.cpp
blob554c869fcc18b5c11de335c2a972949764afeef1
1 #include "stdafx.h"
2 #include "dnsmx.h"
4 #define ASCII_NULL '\0'
5 #define MAXHOSTNAME 256
6 #define BUFSIZE 2048
8 // #pragma comment ( lib, "ws2_32.lib" )
10 /* DNS Header Format
12 * All DNS Message Formats have basically the same structure
13 * (note that an RR (DNS Resource Record) is described in
14 * the other structures that follow this header description):
16 * +--------------------------------+
17 * | DNS Header: <defined below> |
18 * +--------------------------------+
19 * | Question: type of query |
20 * | QNAME: <see below> |
21 * | QTYPE: 2-octet RR type |
22 * | QCLASS: 2-octet RR class |
23 * +--------------------------------+
24 * | Answer: RR answer to query |
25 * +--------------------------------+
26 * | Authority: RR for name server |
27 * +--------------------------------+
28 * | Additional: RR(s) other info |
29 * +--------------------------------+
31 * QNAME is a variable length field where each portion of the
32 * "dotted-notation" domain name is replaced by the number of
33 * octets to follow. So, for example, the domain name
34 * "www.sockets.com" is represented by:
36 * 0 1
37 * octet 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
38 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 * |3|w|w|w|7|s|o|c|k|e|t|s|3|c|o|m|0|
40 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42 * NOTE: The last section, "Additional," often contains records
43 * for queries the server anticipates will be sent (to reduce
44 * traffic). For example, a response to an MX query, would
45 * usually have the A record in additional information.
47 typedef struct dns_hdr
49 USHORT dns_id; /* client query ID number */
50 USHORT dns_flags; /* qualify contents <see below> */
51 USHORT dns_q_count; /* number of questions */
52 USHORT dns_rr_count; /* number of answer RRs */
53 USHORT dns_auth_count; /* number of authority RRs */
54 USHORT dns_add_count; /* number of additional RRs */
55 } DNS_HDR, *PDNS_HDR, FAR *LPDNS_HDR;
57 #define DNS_HDR_LEN 12
59 /* DNS Flags field values
61 * bits: 0 1-4 5 6 7 8 9-11 12-15
62 * +----+--------+----+----+----+----+--------+-------+
63 * | QR | opcode | AA | TC | RD | RA | <zero> | rcode |
64 * +----+--------+----+----+----+----+--------+-------+
66 * QR: 0 for query, and 1 for response
67 * opcode: type of query (0: standard, and 1: inverse query)
68 * AA: set if answer from domain authority
69 * TC: set if message had to be truncated
70 * RD: set if recursive query desired
71 * RA: set if recursion is available from server
72 * <zero>: reserved field
73 * rcode: resulting error non-zero value from authoritative
74 * server (0: no error, 3: name does not exist)
76 #define DNS_FLAG_QR 0x8SpeedPostEmail
77 #define DNS_FLAG_AA 0x0400
78 #define DNS_FLAG_TC 0x0200
79 #define DNS_FLAG_RD 0x0100
80 #define DNS_FLAG_RA 0x0080
81 #define DNS_RCODE_MASK 0xSpeedPostEmailF
82 #define DNS_OPCODE_MASK 0x7800
84 /* DNS Opcode (type of query) */
85 char *DNS_Opcode[] =
87 "Standard Query", /* 0: QUERY */
88 "Inverse Query", /* 1: IQUERY */
89 "Server Status Request", /* 2: STATUS */
92 /* DNS Response Codes (error descriptions) */
93 char *DNS_RCode[] =
95 "No Error", /* 0: ok */
96 "Format Error", /* 1: bad query */
97 "Server Failure", /* 2: server is hosed */
98 "Name Error", /* 3: name doesn't exist (authoritative) */
99 "Not Implemented", /* 4: server doesn't support query */
100 "Refused" /* 5: server refused request */
103 /* DNS Generic Resource Record format (from RFC 1034 and 1035)
105 * NOTE: The first field in the DNS RR Record header is always
106 * the domain name in QNAME format (see earlier description)
108 typedef struct dns_rr_hdr
110 USHORT rr_type; /* RR type code (e.g. A, MX, NS, etc.) */
111 USHORT rr_class; /* RR class code (IN for Internet) */
112 ULONG rr_ttl; /* Time-to-live for resource */
113 USHORT rr_rdlength; /* length of RDATA field (in octets) */
114 USHORT rr_rdata; /* (fieldname used as a ptr) */
115 } DNS_RR_HDR, *PDNS_RR_HDR, FAR *LPDNS_RR_HDR;
117 #define DNS_RR_HDR_LEN 12
119 /* DNS Resource Record RDATA Field Descriptions
121 * The RDATA field contains resource record data associated
122 * with the specified domain name
124 * Type Value Description
125 * -------------------------------------------------------------
126 * A 1 IP Address (32-bit IP version 4)
127 * NS 2 Name server QNAME (for referrals & recursive queries)
128 * CNAME 5 Canonical name of an alias (in QNAME format)
129 * SOA 6 Start of Zone Transfer (see definition below)
130 * WKS 11 Well-known services (see definition below)
131 * PTR 12 QNAME pointing to other nodes (e.g. in inverse lookups)
132 * HINFO 13 Host Information (CPU string, then OS string)
133 * MX 15 Mail server preference and QNAME (see below)
135 char *DNS_RR_Type [] =
137 "<invalid>",
138 "A", // 1: Host Address
139 "NS", // 2: Authoritative Name Server
140 "MD", // 3: <obsolete>
141 "MF", // 4: <obsolete>
142 "CNAME", // 5: The true, canonical name for an alias
143 "SOA", // 6: Start-of-Zone of authority record
144 "MB", // 7: Mailbox <experimental>
145 "MG", // 8: Mailgroup <experimental>
146 "MR", // 9: Mail Rename Domain Name <experimental>
147 "NULL", // 10: NULL Resource Record <experimental>
148 "WKS", // 11: Well-known service description
149 "PTR", // 12: Domain Name Pointer
150 "HINFO", // 13: Host Information
151 "MINFO", // 14: Mailbox or Mail List information
152 "MX", // 15: Mail Exchange (from RFC 974)
153 "TXT" // 16: Text String
156 #define DNS_RRTYPE_A 1
157 #define DNS_RRTYPE_NS 2
158 #define DNS_RRTYPE_CNAME 5
159 #define DNS_RRTYPE_SOA 6
160 #define DNS_RRTYPE_WKS 11
161 #define DNS_RRTYPE_PTR 12
162 #define DNS_RRTYPE_HINFO 13
163 #define DNS_RRTYPE_MX 15
165 /* DNS Resource Record Classes:
167 * One almost always uses Internet RR Class (also note: the
168 * class value 255 denotes a wildcard, all classes)
170 char *DNS_RR_Class [] =
172 "<invalid>",
173 "IN", // 1: Internet - used for most queries!
174 "CS", // 2: CSNET <obsolete>
175 "CH", // 3: CHAOS Net
176 "HS" // 4: Hesiod
179 #define DNS_RRCLASS_IN 1
180 #define DNS_RRCLASS_CS 2
181 #define DNS_RRCLASS_CH 3
182 #define DNS_RRCLASS_HS 4
184 /* DNS SOA Resource Data Field
186 * NOTE: First two fields not shown here. They are:
187 * MNAME: QNAME of primary server for this zone
188 * RNAME: QNAME of mailbox of admin for this zone
190 typedef struct dns_rdata_soa
192 ULONG soa_serial; /* data version for this zone */
193 ULONG soa_refresh; /* time-to-live for data (in seconds) */
194 ULONG soa_retry; /* time between retrieds (in seconds) */
195 ULONG soa_expire; /* time until zone not auth (in seconds) */
196 ULONG soa_minimum; /* default TTL for RRs (in seconds) */
197 } DNS_RDATA_SOA, PDNS_RDATA_SOA, FAR *LPDNS_RDATA_SOA;
199 #define DNS_SOA_LEN 20
201 /* DNS WKS Resource Data Field (RFC 1035)
203 * NOTE: The bitmap field is variable length, with as many
204 * octets necessary to indicate the bit field for the port
205 * number.
207 typedef struct dns_rdata_wks
209 ULONG wks_addr; /* IPv4 address */
210 UCHAR wks_protocol; /* Protocol (e.g. 6=TCP, 17=UDP) */
211 UCHAR wks_bitmap; /* e.g. bit 26 = SMTP (port 25) */
212 } DNS_RDATA_WKS, *PDNS_RDATA_WKS, FAR *LPDNS_RDATA_WKS;
214 #define DNS_WKX_LEN 6
216 /* DNS MX Resource Data Field
218 typedef struct dns_rdata_mx
220 USHORT mx_pref; /* Preference value */
221 USHORT mx_xchange; /* QNAME (field used as ptr) */
222 } DNS_RDATA_MX, *PDNS_RDATA_MX, FAR *LPDNS_RDATA_MX;
224 #define DNS_MX_LEN 4
226 /* Variables used for DNS Header construction & parsing */
227 PDNS_HDR pDNShdr;
228 PDNS_RR_HDR pDNS_RR;
229 PDNS_RDATA_SOA pDNS_SOA;
230 PDNS_RDATA_WKS pDNS_WKS;
231 PDNS_RDATA_MX pDNS_MX;
233 /* For Parsing Names in a Reply */
234 #define INDIR_MASK 0xc0
236 /* Number of bytes of fixed size data in query structure */
237 #define QFIXEDSZ 4
238 /* number of bytes of fixed size data in resource record */
239 #define RRFIXEDSZ 10
241 /* Processor Types */
242 char *aszProcessor[] =
247 "Intel 386",
248 "Intel 486",
249 "Intel Pentium"
253 void GetQName( char FAR *pszHostName, char FAR *pQName );
254 void PrintQName( char FAR *pQName );
255 int PutQName( char FAR *pszHostName, char FAR *pQName );
257 USHORT _getshort(char *msgp)
259 register UCHAR *p = (UCHAR *) msgp;
260 register USHORT u;
262 u = *p++ << 8;
263 return ((USHORT)(u | *p));
266 ULONG _getlong(char *msgp)
268 register UCHAR *p = (UCHAR *) msgp;
269 register ULONG u;
271 u = *p++; u <<= 8;
272 u |= *p++; u <<= 8;
273 u |= *p++; u <<= 8;
274 return (u | *p);
279 * Expand compressed domain name 'comp_dn' to full domain name.
280 * 'msg' is a pointer to the begining of the message,
281 * 'eomorig' points to the first location after the message,
282 * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
283 * Return size of compressed name or -1 if there was an error.
285 int dn_expand(char *msg,char *eomorig,char *comp_dn,char *exp_dn,int length)
287 register char *cp, *dn;
288 register int n, c;
289 char *eom;
290 int len = -1, checked = 0;
292 dn = exp_dn;
293 cp = comp_dn;
294 eom = exp_dn + length - 1;
296 * fetch next label in domain name
298 while (n = *cp++) {
300 * Check for indirection
302 switch (n & INDIR_MASK) {
303 case 0:
304 if (dn != exp_dn) {
305 if (dn >= eom)
306 return (-1);
307 *dn++ = '.';
309 if (dn+n >= eom)
310 return (-1);
311 checked += n + 1;
312 while (--n >= 0) {
313 if ((c = *cp++) == '.') {
314 if (dn+n+1 >= eom)
315 return (-1);
316 *dn++ = '\\';
318 *dn++ = c;
319 if (cp >= eomorig) /* out of range */
320 return(-1);
322 break;
324 case INDIR_MASK:
325 if (len < 0)
326 len = cp - comp_dn + 1;
327 cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
328 if (cp < msg || cp >= eomorig) /* out of range */
329 return(-1);
330 checked += 2;
332 * Check for loops in the compressed name;
333 * if we've looked at the whole message,
334 * there must be a loop.
336 if (checked >= eomorig - msg)
337 return (-1);
338 break;
340 default:
341 return (-1); /* flag error */
344 *dn = '\0';
345 if (len < 0)
346 len = cp - comp_dn;
347 return (len);
351 * Skip over a compressed domain name. Return the size or -1.
353 int dn_skipname(UCHAR *comp_dn, UCHAR *eom)
355 register UCHAR *cp;
356 register int n;
358 cp = comp_dn;
359 while (cp < eom && (n = *cp++)) {
361 * check for indirection
363 switch (n & INDIR_MASK) {
364 case 0: /* normal case, n == len */
365 cp += n;
366 continue;
367 default: /* illegal type */
368 return (-1);
369 case INDIR_MASK: /* indirection */
370 cp++;
372 break;
374 return (cp - comp_dn);
378 // ÉèÖÃ×èÈûģʽ
380 BOOL SetBlockingMode ( SOCKET hSocket, BOOL bNonblockingEnable )
382 if ( hSocket == INVALID_SOCKET || hSocket == 0 )
384 return FALSE;
387 long cmd = FIONBIO;
388 long zero = 0;
389 u_long* argp = NULL;
390 if ( bNonblockingEnable )
391 argp = (u_long*)&cmd;
392 else
393 argp = (u_long*)&zero;
394 int err = ioctlsocket ( hSocket, cmd, argp );
395 if ( err != 0 )
397 return FALSE;
400 return TRUE;
404 BOOL GetMX (
405 char *pszQuery,
406 char *pszServer,
407 OUT t_Ary_MXHostInfos &Ary_MXHostInfos
410 SOCKET hSocket;
411 SOCKADDR_IN stSockAddr; // socket address structures
412 int nAddrLen = sizeof( SOCKADDR_IN );
414 HOSTENT *pHostEnt;
416 char achBufOut[ BUFSIZE ] = { 0 };
417 char achBufIn[ BUFSIZE ] = { 0 };
418 int nQueryLen = 0;
419 int nRC;
421 char *p, *np, name[128], *eom;
422 int count, j, i, n;
424 memset( &stSockAddr, ASCII_NULL, sizeof( stSockAddr ) );
426 stSockAddr.sin_family = AF_INET;
427 stSockAddr.sin_port = htons( 53);
428 stSockAddr.sin_addr.s_addr = inet_addr( pszServer );
429 if ( stSockAddr.sin_addr.s_addr == INADDR_NONE )
431 pHostEnt = gethostbyname( pszServer );
432 if ( pHostEnt )
434 stSockAddr.sin_addr.s_addr = *((ULONG *)pHostEnt->h_addr_list[0]);
436 else
438 return FALSE;
439 } // end if
440 } // end if
443 /*------------------------------------------------------------
444 * Get a DGRAM socket
447 hSocket = socket( AF_INET, SOCK_DGRAM, 0 );
449 if ( hSocket == INVALID_SOCKET )
451 return FALSE;
452 } // end if
454 /*-----------------------------------------------------------
455 * Format DNS Query
458 pDNShdr = (PDNS_HDR)&( achBufOut[ 0 ] );
459 pDNShdr->dns_id = htons( 0xDEAD );
460 pDNShdr->dns_flags = htons( DNS_FLAG_RD ); // do recurse
461 pDNShdr->dns_q_count = htons( 1 ); // one query
462 pDNShdr->dns_rr_count = 0; // none in query
463 pDNShdr->dns_auth_count = 0; // none in query
464 pDNShdr->dns_add_count = 0; // none in query
466 nQueryLen = PutQName( pszQuery, &(achBufOut[ DNS_HDR_LEN ] ) );
467 nQueryLen += DNS_HDR_LEN;
469 achBufOut[ nQueryLen++ ] = 0;
470 achBufOut[ nQueryLen++ ] = 0;
471 achBufOut[ nQueryLen ] = DNS_RRTYPE_MX;
472 achBufOut[ nQueryLen + 1 ] = 0;
473 achBufOut[ nQueryLen + 2 ] = DNS_RRCLASS_IN;
474 achBufOut[ nQueryLen + 3 ] = 0;
476 nQueryLen += 4;
478 /*-----------------------------------------------------------
479 * Send DNS Query to server
482 nRC = sendto( hSocket,
483 achBufOut,
484 nQueryLen,
486 (LPSOCKADDR)&stSockAddr,
487 sizeof( SOCKADDR_IN ) );
489 if ( nRC == SOCKET_ERROR )
492 closesocket( hSocket );
493 return FALSE;
495 else
500 // VERIFY ( SetBlockingMode ( hSocket, TRUE ) );
502 // Óà select Ä£ÐÍʵÏÖÁ¬½Ó³¬Ê±
503 struct timeval timeout;
504 fd_set r;
505 FD_ZERO(&r);
506 FD_SET(hSocket, &r);
507 timeout.tv_sec = 5; //Á¬½Ó³¬Ê±Ãë
508 timeout.tv_usec =0;
509 int ret = select(0, &r, 0, 0, &timeout);
510 if ( ret == SOCKET_ERROR )
512 ::closesocket(hSocket);
513 hSocket = SOCKET_ERROR;
514 return FALSE;
517 // µÃµ½¿É¶ÁµÄÊý¾Ý³¤¶È
518 long cmd = FIONREAD;
519 u_long argp = 0;
520 BOOL err = ioctlsocket ( hSocket, cmd, (u_long*)&argp );
521 if ( err || argp < 1 )
523 ::closesocket(hSocket);
524 hSocket = SOCKET_ERROR;
525 return FALSE;
528 nRC = recvfrom( hSocket,
529 achBufIn,
530 BUFSIZE,
532 (LPSOCKADDR)&stSockAddr,
533 &nAddrLen );
535 if ( nRC == SOCKET_ERROR )
537 int nWSAErr = WSAGetLastError();
539 if ( nWSAErr != WSAETIMEDOUT )
542 closesocket( hSocket );
543 return FALSE;
545 else
548 closesocket( hSocket );
549 return FALSE;
552 else
554 pDNShdr = (PDNS_HDR)&( achBufIn[ 0 ] );
555 p = (char *)&pDNShdr[0];
556 p+=12;
557 count = (int)*p;
559 // Parse the Question...
560 for (i = 0; i< ntohs(pDNShdr->dns_q_count); i++)
562 np = name;
563 eom = (char *)pDNShdr+nRC;
565 if ( (n = dn_expand((char *)pDNShdr, eom, p, name, 127)) < 0 )
567 return FALSE;
570 p += n + QFIXEDSZ;
573 for (i = 0; i< ntohs(pDNShdr->dns_rr_count); i++)
576 // The Question Name appears Again...
577 if ((n = dn_expand((char *)pDNShdr, eom, p, name, 127)) < 0)
579 return FALSE;
581 p+=n;
584 j = _getshort(p);; //TYPE
585 p+=2;
586 //printf("%s\tType:%d", name, j);
588 j = _getshort(p); //CLASS
589 p+=2;
590 // printf("\tClass:%d", j);
592 j = _getlong(p); //TTL
593 p+=4;
594 // printf("\tTTL:%d", j);
596 j = _getshort(p); //RDLENGTH
597 p+=2;
598 // printf("\tRDLENGTH:%d", j);
600 j = _getshort(p); //N??
601 p+=2;
603 // This should be an MX Name...
604 if ( (n = dn_expand((char *)pDNShdr, eom, p, name, 127)) < 0 )
606 return FALSE;
609 t_MXHostInfo tMXHostInfo = {0};
610 strncpy ( (char*)tMXHostInfo.szMXHost, name, _countof(tMXHostInfo.szMXHost));
611 tMXHostInfo.N = j;
612 Ary_MXHostInfos.Add ( tMXHostInfo );
613 TRACE ( _T("%s\t%d\r\n"), name, j );
614 p += n;
616 return TRUE;
622 closesocket( hSocket );
623 return FALSE;
627 void GetQName( char FAR *pszHostName, char FAR *pQName )
630 int i, j, k;
632 for ( i = 0; i < BUFSIZE; i++ )
634 j = *pQName;
636 if ( j == 0 )
637 break;
639 for ( k = 1; k <= j; k++ )
641 *pszHostName++ = *( pQName + i + k );
642 } // end for loop
643 } // end for loop
645 *pszHostName++ = ASCII_NULL;
646 } /* end GetQName() */
649 void PrintQName( char FAR *pQName )
651 int i, j, k;
653 for ( i = 0; i < BUFSIZE; i++ )
655 j = *pQName;
657 if ( j == 0 )
658 break;
660 for ( k = 1; k <= j; k++ )
662 //printf( "%c", *( pQName + i + k ) );
663 } // end for loop
664 } // end for loop
665 } /* end PrintQName() */
668 int PutQName( char FAR *pszHostName, char FAR *pQName )
670 int i;
671 int j = 0;
672 int k = 0;
675 for ( i = 0; *( pszHostName + i ); i++ )
677 char c = *( pszHostName + i ); /* get next character */
680 if ( c == '.' )
682 /* dot encountered, fill in previous length */
683 *( pQName + j ) = k;
685 k = 0; /* reset segment length */
686 j = i + 1; /* set index to next counter */
688 else
690 *( pQName + i + 1 ) = c; /* assign to QName */
691 k++; /* inc count of seg chars */
692 } // end if
693 } // end for loop
695 *(pQName + j ) = k; /* count for final segment */
696 *(pQName + i + 1 ) = 0; /* count for trailing NULL segment is 0 */
698 return ( i + 1 ); /* return total length of QName */
702 // ³¢ÊÔËùÓеÄDNSÀ´²éѯÓʾַþÎñÆ÷µØÖ·
704 BOOL GetMX (
705 char *pszQuery, // Òª²éѯµÄÓòÃû
706 OUT t_Ary_MXHostInfos &Ary_MXHostInfos // Êä³ö Mail Exchange Ö÷»úÃû
709 CNetAdapterInfo m_NetAdapterInfo;
710 m_NetAdapterInfo.Refresh ();
711 int nNetAdapterCount = m_NetAdapterInfo.GetNetCardCount();
712 for ( int i=0; i<nNetAdapterCount; i++ )
714 COneNetAdapterInfo *pOneNetAdapterInfo = m_NetAdapterInfo.Get_OneNetAdapterInfo ( i );
715 if ( pOneNetAdapterInfo )
717 int nDNSCount = pOneNetAdapterInfo->Get_DNSCount ();
718 for ( int j=0; j<nDNSCount; j++ )
720 CString csDNS = pOneNetAdapterInfo->Get_DNSAddr ( j );
721 if ( GetMX ( pszQuery, csDNS.GetBuffer(0), Ary_MXHostInfos ) )
723 return TRUE;
729 return FALSE;