www.pudn.com > mailserver-muiter.zip > dnsmx.cpp
/* * The code I found by Bob Quinn was not complete - I hacked it to * completion just to see what the issues were with getting MX entries. * * Part of the hacking involved extracting code from Unix BIND which * does the low level packet parsing. (The dn_ routines) * * It needs to determine the DNS server's IP address - right now it is * hard coded into this program. NSLOOKUP probably looks in the * registry for that value. * * - David */ /*--------------------------------------------------------------------- * * Program: DNS_QRY.EXE Query DNS Server, and parse response * * filename: DNS_QRY.c * * Copyright by Bob Quinn, 1997 http://www.sockets.com * * Description: * Not a test application, per se, but designed to allow custom * queries of DNS records by actually sending and receiving DNS * messages, rather than relying on the hostname resolution APIs * to do it for me. The big advantage is that I can ask for any * DNS record types that I want. The downside is that I have to * hard-code the DNS server myself, or peak at the implementation's * configuration (e.g. MSTCP) for the config'd server(s). * * I started creating this in response to a Frequently Asked Question * on one of the WinSock mailing lists for a way to use WinSock to * retrieve an MX record. * * NOTE: Eventually, I'd like to provide the same command-line * capabilities as available from BSD Unix NSLOOKUP utility. * * Editing History: * 8/17/97 rcq started work -- STOPPED BEFORE GOT IT WORKING * (serious byte order problems and buffer/format * management problems in general, causing Format Err). * 2/15/98 mjd got query and reply working, working on decoding reply * * * ---------------------------------------------------------------------*/ #include "stdafx.h" #include "tools.h" #include#define ASCII_NULL '\0' #define MAXHOSTNAME 256 #define BUFSIZE 2048 //#define BUFSIZE 4096 /* DNS Header Format * * All DNS Message Formats have basically the same structure * (note that an RR (DNS Resource Record) is described in * the other structures that follow this header description): * * +--------------------------------+ * | DNS Header: | * +--------------------------------+ * | Question: type of query | * | QNAME: | * | QTYPE: 2-octet RR type | * | QCLASS: 2-octet RR class | * +--------------------------------+ * | Answer: RR answer to query | * +--------------------------------+ * | Authority: RR for name server | * +--------------------------------+ * | Additional: RR(s) other info | * +--------------------------------+ * * QNAME is a variable length field where each portion of the * "dotted-notation" domain name is replaced by the number of * octets to follow. So, for example, the domain name * "www.sockets.com" is represented by: * * 0 1 * octet 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |3|w|w|w|7|s|o|c|k|e|t|s|3|c|o|m|0| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * NOTE: The last section, "Additional," often contains records * for queries the server anticipates will be sent (to reduce * traffic). For example, a response to an MX query, would * usually have the A record in additional information. */ typedef struct dns_hdr { u_short dns_id; /* client query ID number */ u_short dns_flags; /* qualify contents */ u_short dns_q_count; /* number of questions */ u_short dns_rr_count; /* number of answer RRs */ u_short dns_auth_count; /* number of authority RRs */ u_short dns_add_count; /* number of additional RRs */ } DNS_HDR, *PDNS_HDR, FAR *LPDNS_HDR; #define DNS_HDR_LEN 12 /* DNS Flags field values * * bits: 0 1-4 5 6 7 8 9-11 12-15 * +----+--------+----+----+----+----+--------+-------+ * | QR | opcode | AA | TC | RD | RA | | rcode | * +----+--------+----+----+----+----+--------+-------+ * * QR: 0 for query, and 1 for response * opcode: type of query (0: standard, and 1: inverse query) * AA: set if answer from domain authority * TC: set if message had to be truncated * RD: set if recursive query desired * RA: set if recursion is available from server * : reserved field * rcode: resulting error non-zero value from authoritative * server (0: no error, 3: name does not exist) */ #define DNS_FLAG_QR 0x8000 #define DNS_FLAG_AA 0x0400 #define DNS_FLAG_TC 0x0200 #define DNS_FLAG_RD 0x0100 #define DNS_FLAG_RA 0x0080 #define DNS_RCODE_MASK 0x000F #define DNS_OPCODE_MASK 0x7800 /* DNS Opcode (type of query) */ char *DNS_Opcode[] = { "Standard Query", /* 0: QUERY */ "Inverse Query", /* 1: IQUERY */ "Server Status Request", /* 2: STATUS */ }; /* DNS Response Codes (error descriptions) */ char *DNS_RCode[] = { "No Error", /* 0: ok */ "Format Error", /* 1: bad query */ "Server Failure", /* 2: server is hosed */ "Name Error", /* 3: name doesn't exist (authoritative) */ "Not Implemented", /* 4: server doesn't support query */ "Refused" /* 5: server refused request */ }; /* DNS Generic Resource Record format (from RFC 1034 and 1035) * * NOTE: The first field in the DNS RR Record header is always * the domain name in QNAME format (see earlier description) */ typedef struct dns_rr_hdr { u_short rr_type; /* RR type code (e.g. A, MX, NS, etc.) */ u_short rr_class; /* RR class code (IN for Internet) */ u_long rr_ttl; /* Time-to-live for resource */ u_short rr_rdlength; /* length of RDATA field (in octets) */ u_short rr_rdata; /* (fieldname used as a ptr) */ } DNS_RR_HDR, *PDNS_RR_HDR, FAR *LPDNS_RR_HDR; #define DNS_RR_HDR_LEN 12 /* DNS Resource Record RDATA Field Descriptions * * The RDATA field contains resource record data associated * with the specified domain name * * Type Value Description * ------------------------------------------------------------- * A 1 IP Address (32-bit IP version 4) * NS 2 Name server QNAME (for referrals & recursive queries) * CNAME 5 Canonical name of an alias (in QNAME format) * SOA 6 Start of Zone Transfer (see definition below) * WKS 11 Well-known services (see definition below) * PTR 12 QNAME pointing to other nodes (e.g. in inverse lookups) * HINFO 13 Host Information (CPU string, then OS string) * MX 15 Mail server preference and QNAME (see below) */ char *DNS_RR_Type [] = { " ", "A", // 1: Host Address "NS", // 2: Authoritative Name Server "MD", // 3: "MF", // 4: "CNAME", // 5: The true, canonical name for an alias "SOA", // 6: Start-of-Zone of authority record "MB", // 7: Mailbox "MG", // 8: Mailgroup "MR", // 9: Mail Rename Domain Name "NULL", // 10: NULL Resource Record "WKS", // 11: Well-known service description "PTR", // 12: Domain Name Pointer "HINFO", // 13: Host Information "MINFO", // 14: Mailbox or Mail List information "MX", // 15: Mail Exchange (from RFC 974) "TXT" // 16: Text String }; #define DNS_RRTYPE_A 1 #define DNS_RRTYPE_NS 2 #define DNS_RRTYPE_CNAME 5 #define DNS_RRTYPE_SOA 6 #define DNS_RRTYPE_WKS 11 #define DNS_RRTYPE_PTR 12 #define DNS_RRTYPE_HINFO 13 #define DNS_RRTYPE_MX 15 /* DNS Resource Record Classes: * * One almost always uses Internet RR Class (also note: the * class value 255 denotes a wildcard, all classes) */ char *DNS_RR_Class [] = { " ", "IN", // 1: Internet - used for most queries! "CS", // 2: CSNET "CH", // 3: CHAOS Net "HS" // 4: Hesiod }; #define DNS_RRCLASS_IN 1 #define DNS_RRCLASS_CS 2 #define DNS_RRCLASS_CH 3 #define DNS_RRCLASS_HS 4 /* DNS SOA Resource Data Field * * NOTE: First two fields not shown here. They are: * MNAME: QNAME of primary server for this zone * RNAME: QNAME of mailbox of admin for this zone */ typedef struct dns_rdata_soa { u_long soa_serial; /* data version for this zone */ u_long soa_refresh; /* time-to-live for data (in seconds) */ u_long soa_retry; /* time between retrieds (in seconds) */ u_long soa_expire; /* time until zone not auth (in seconds) */ u_long soa_minimum; /* default TTL for RRs (in seconds) */ } DNS_RDATA_SOA, PDNS_RDATA_SOA, FAR *LPDNS_RDATA_SOA; #define DNS_SOA_LEN 20 /* DNS WKS Resource Data Field (RFC 1035) * * NOTE: The bitmap field is variable length, with as many * octets necessary to indicate the bit field for the port * number. */ typedef struct dns_rdata_wks { u_long wks_addr; /* IPv4 address */ u_char wks_protocol; /* Protocol (e.g. 6=TCP, 17=UDP) */ u_char wks_bitmap; /* e.g. bit 26 = SMTP (port 25) */ } DNS_RDATA_WKS, *PDNS_RDATA_WKS, FAR *LPDNS_RDATA_WKS; #define DNS_WKX_LEN 6 /* DNS MX Resource Data Field */ typedef struct dns_rdata_mx { u_short mx_pref; /* Preference value */ u_short mx_xchange; /* QNAME (field used as ptr) */ } DNS_RDATA_MX, *PDNS_RDATA_MX, FAR *LPDNS_RDATA_MX; #define DNS_MX_LEN 4 /* Variables used for DNS Header construction & parsing */ PDNS_HDR pDNShdr; PDNS_RR_HDR pDNS_RR; PDNS_RDATA_SOA pDNS_SOA; PDNS_RDATA_WKS pDNS_WKS; PDNS_RDATA_MX pDNS_MX; /* For Parsing Names in a Reply */ #define INDIR_MASK 0xc0 /* Number of bytes of fixed size data in query structure */ #define QFIXEDSZ 4 /* number of bytes of fixed size data in resource record */ #define RRFIXEDSZ 10 /* Processor Types */ char *aszProcessor[] = { "", "", "", "Intel 386", "Intel 486", "Intel Pentium" }; void GetQName( char FAR *pszHostName, char FAR *pQName ); void PrintQName( char FAR *pQName ); int PutQName( char FAR *pszHostName, char FAR *pQName ); u_short _getshort(char *msgp) { register u_char *p = (u_char *) msgp; register u_short u; u = *p++ << 8; return ((u_short)(u | *p)); } u_long _getlong(char *msgp) { register u_char *p = (u_char *) msgp; register u_long u; u = *p++; u <<= 8; u |= *p++; u <<= 8; u |= *p++; u <<= 8; return (u | *p); } /* * Expand compressed domain name 'comp_dn' to full domain name. * 'msg' is a pointer to the begining of the message, * 'eomorig' points to the first location after the message, * 'exp_dn' is a pointer to a buffer of size 'length' for the result. * Return size of compressed name or -1 if there was an error. */ int dn_expand(char *msg,char *eomorig,char *comp_dn,char *exp_dn,int length) { register char *cp, *dn; register int n, c; char *eom; int len = -1, checked = 0; dn = exp_dn; cp = comp_dn; eom = exp_dn + length - 1; /* * fetch next label in domain name */ while (n = *cp++) { /* * Check for indirection */ switch (n & INDIR_MASK) { case 0: if (dn != exp_dn) { if (dn >= eom) return (-1); *dn++ = '.'; } if (dn+n >= eom) return (-1); checked += n + 1; while (--n >= 0) { if ((c = *cp++) == '.') { if (dn+n+1 >= eom) return (-1); *dn++ = '\\'; } *dn++ = c; if (cp >= eomorig) /* out of range */ return(-1); } break; case INDIR_MASK: if (len < 0) len = cp - comp_dn + 1; cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff)); if (cp < msg || cp >= eomorig) /* out of range */ return(-1); checked += 2; /* * Check for loops in the compressed name; * if we've looked at the whole message, * there must be a loop. */ if (checked >= eomorig - msg) return (-1); break; default: return (-1); /* flag error */ } } *dn = '\0'; if (len < 0) len = cp - comp_dn; return (len); } /* * Skip over a compressed domain name. Return the size or -1. */ dn_skipname(u_char *comp_dn, u_char *eom) { register u_char *cp; register int n; cp = comp_dn; while (cp < eom && (n = *cp++)) { /* * check for indirection */ switch (n & INDIR_MASK) { case 0: /* normal case, n == len */ cp += n; continue; default: /* illegal type */ return (-1); case INDIR_MASK: /* indirection */ cp++; } break; } return (cp - comp_dn); } char* GetMX( char *pszQuery, char *pszServer, int minlevel ) { WSADATA stWSAData; WORD wWSAVersion = 0x0202; SOCKET hSocket; SOCKADDR_IN stSockAddr; // socket address structures int nAddrLen = sizeof( SOCKADDR_IN ); HOSTENT *pHostEnt; char achBufOut[ BUFSIZE ] = { 0 }; char achBufIn[ BUFSIZE ] = { 0 }; int nQueryLen = 0; int nRC; char *p, *np, name[128], *eom; int count, j, i, n; nRC = WSAStartup( wWSAVersion, &stWSAData ); if ( nRC ) { return NULL; } memset( &stSockAddr, ASCII_NULL, sizeof( stSockAddr ) ); stSockAddr.sin_family = AF_INET; stSockAddr.sin_port = htons( 53); stSockAddr.sin_addr.s_addr = inet_addr( pszServer ); if ( stSockAddr.sin_addr.s_addr == INADDR_NONE ) { pHostEnt = gethostbyname( pszServer ); if ( pHostEnt ) { stSockAddr.sin_addr.s_addr = *((u_long *)pHostEnt->h_addr_list[0]); } else { WSACleanup(); return NULL; } // end if } // end if /*------------------------------------------------------------ * Get a DGRAM socket */ hSocket = socket( AF_INET, SOCK_DGRAM, 0 ); if ( hSocket == INVALID_SOCKET ) { WSACleanup(); return NULL; } // end if /*----------------------------------------------------------- * Format DNS Query * * <<8/17/97 hard-coded test case: MX Record>> */ pDNShdr = (PDNS_HDR)&( achBufOut[ 0 ] ); pDNShdr->dns_id = htons( 0xDEAD ); pDNShdr->dns_flags = htons( DNS_FLAG_RD ); // do recurse pDNShdr->dns_q_count = htons( 1 ); // one query pDNShdr->dns_rr_count = 0; // none in query pDNShdr->dns_auth_count = 0; // none in query pDNShdr->dns_add_count = 0; // none in query nQueryLen = PutQName( pszQuery, &(achBufOut[ DNS_HDR_LEN ] ) ); nQueryLen += DNS_HDR_LEN; achBufOut[ nQueryLen++ ] = 0; achBufOut[ nQueryLen++ ] = 0; /* BQ NOTE: I'm cheating on this one! (should be done cleaner)*/ achBufOut[ nQueryLen ] = DNS_RRTYPE_MX; achBufOut[ nQueryLen + 1 ] = 0; achBufOut[ nQueryLen + 2 ] = DNS_RRCLASS_IN; achBufOut[ nQueryLen + 3 ] = 0; nQueryLen += 4; /*----------------------------------------------------------- * Send DNS Query to server */ nRC = sendto( hSocket, achBufOut, nQueryLen, 0, (LPSOCKADDR)&stSockAddr, sizeof( SOCKADDR_IN ) ); if ( nRC == SOCKET_ERROR ) { closesocket( hSocket ); WSACleanup(); return NULL; } else { } // end if /*------------------------------------------ * Recvfrom() */ nRC = recvfrom( hSocket, achBufIn, BUFSIZE, 0, (LPSOCKADDR)&stSockAddr, &nAddrLen ); if ( nRC == SOCKET_ERROR ) { int nWSAErr = WSAGetLastError(); if ( nWSAErr != WSAETIMEDOUT ) { closesocket( hSocket ); WSACleanup(); return NULL; } else { closesocket( hSocket ); WSACleanup(); return NULL; } } else { pDNShdr = (PDNS_HDR)&( achBufIn[ 0 ] ); p = (char *)&pDNShdr[0]; p+=12; count = (int)*p; // Parse the Question... for (i = 0; i< ntohs(pDNShdr->dns_q_count); i++) { np = name; eom = (char *)pDNShdr+nRC; if ((n = dn_expand((char *)pDNShdr, eom, p, name, 127)) < 0) { return NULL; } p += n + QFIXEDSZ; } //We store the mx's in a 4096 characters list char *mxstr; mxstr = (char *) HeapAlloc( GetProcessHeap(),HEAP_ZERO_MEMORY,4096); sprintf(mxstr,""); for (i = 0; i< ntohs(pDNShdr->dns_rr_count); i++) { // The Question Name appears Again... if ((n = dn_expand((char *)pDNShdr, eom, p, name, 127)) < 0) { return NULL; } p+=n; j = _getshort(p);; //TYPE p+=2; //printf("%s\tType:%d", name, j); j = _getshort(p); //CLASS p+=2; // printf("\tClass:%d", j); j = _getlong(p); //TTL p+=4; // printf("\tTTL:%d", j); j = _getshort(p); //RDLENGTH p+=2; // printf("\tRDLENGTH:%d", j); j = _getshort(p); //N?? p+=2; // This should be an MX Name... if ((n = dn_expand((char *)pDNShdr, eom, p, name, 127)) < 0) { } if (i == 0) { sprintf(mxstr,"%s %d",name,j); } else { sprintf(mxstr,"%s\n%s %d",mxstr,name,j); } p += n; } return mxstr; } /* end else (recvfrom() succeeded) */ closesocket( hSocket ); WSACleanup(); return NULL; } /* end main() */ void GetQName( char FAR *pszHostName, char FAR *pQName ) { int i, j, k; for ( i = 0; i < BUFSIZE; i++ ) { j = *pQName; if ( j == 0 ) break; for ( k = 1; k <= j; k++ ) { *pszHostName++ = *( pQName + i + k ); } // end for loop } // end for loop *pszHostName++ = ASCII_NULL; } /* end GetQName() */ void PrintQName( char FAR *pQName ) { int i, j, k; for ( i = 0; i < BUFSIZE; i++ ) { j = *pQName; if ( j == 0 ) break; for ( k = 1; k <= j; k++ ) { //printf( "%c", *( pQName + i + k ) ); } // end for loop } // end for loop } /* end PrintQName() */ int PutQName( char FAR *pszHostName, char FAR *pQName ) { int i; int j = 0; int k = 0; for ( i = 0; *( pszHostName + i ); i++ ) { char c = *( pszHostName + i ); /* get next character */ if ( c == '.' ) { /* dot encountered, fill in previous length */ *( pQName + j ) = k; k = 0; /* reset segment length */ j = i + 1; /* set index to next counter */ } else { *( pQName + i + 1 ) = c; /* assign to QName */ k++; /* inc count of seg chars */ } // end if } // end for loop *(pQName + j ) = k; /* count for final segment */ *(pQName + i + 1 ) = 0; /* count for trailing NULL segment is 0 */ #ifdef DEBUG for ( j = 1; j < 30; j++ ) { } // end for loop #endif return ( i + 1 ); /* return total length of QName */ } /* end PutQName() */ /***********************************************/