Announcement

Collapse
No announcement yet.

Direct SMTP Connections

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Direct SMTP Connections

    Hi Guys,

    Does anyone have experience or sample code that makes direct connections to email recipients instead of connecting to a SMTP server?

    I'm having trouble finding a reliable third-party SMTP server that doesn't cost a bomb and want to investigate this method, which "cuts out the middle man", if you will. After searching around, I know it is possible but may be a lot of work! (the server responses would need to be checked). I have a permanent 4.5mb connection so running an actual SMTP server may be an option.

    Note: I want to use this method for opt-in mailing lists only for my site (www.kgpsoftware.com), private lists, no spam, etc.

    Any thoughts are welcome..
    kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

  • #2
    Kev,

    About 4 years ago I wrote a small program to validate the legitimacy of an EMail address. The idea was to see if the recipient's email server rejected the incoming email (mostly for unknown address errors). It worked pretty good, but I never fully developed it. I'm sure I have the source around here someplace, but it may take a while to search through my achieves.

    The main idea though was pretty easy. Get the domain name from the email address. Open a normal SMTP session using the domain name, and follow the commands just as if you were an SMTP server. I gleaned most of my responses by simply getting the replies of the receiving server and logging what my SMTP servers were sending.

    One of the best EMail server packages I've seen in a long time is hMailServer which is open source. You can install this, turn the logs on, and get all the handshake info you need.

    In the mean time though, I'll see if I can't dig up my code for you.
    Software makes Hardware Happen

    Comment


    • #3
      Kev,

      Well, I've located bits and pieces of the code but not the whole program itself. From what I remember though, the process went like this:
      1. Strip off the domain name from the email address.
      2. Get the 'A' record and all 'MX' records for the domain name
      3. Make a direct connection to the domain using the 'A' and/or 'MX' records
      4. Various tests, sending, etc.

      The "hard" part was getting the MX records. You can get the 'A' record easily enough with
      Code:
      HOST ADDR Domain TO ARec&
      The relevant code for getting the default DNS server address and MX records(which I got from here, I believe it is Borge's code) looks like this:
      Code:
      #INCLUDE "WSOCK32.INC"
      
      %MAX_HOSTNAME_LEN = 128
      %MAX_DOMAIN_NAME_LEN = 128
      %MAX_SCOPE_ID_LEN = 256
      '
      TYPE FIXED_INFO
          HostName(%MAX_HOSTNAME_LEN + 4 - 1)AS BYTE
          DomainName(%MAX_DOMAIN_NAME_LEN + 4 - 1) AS BYTE
          CurrentDnsServer AS LONG
          DnsServerList    AS IP_ADDR_STRING
          NodeType         AS LONG
          ScopeId(%MAX_SCOPE_ID_LEN + 4 - 1) AS BYTE
          EnableRouting    AS LONG
          EnableProxy      AS LONG
          EnableDns        AS LONG
      END TYPE
      ' Declare is done in a manner where we can use loadlibrary in case
      ' PC does not have iphlpapi.dll on it (like old Win 95 systems don't).
      '
      DECLARE FUNCTION GetNetworkParams ALIAS "GetNetworkParams" (BYREF pFixedInfo AS ANY, BYREF pOutBufLen AS LONG) AS LONG
      '
      '
      %ERROR_NOT_SUPPORTED = 50
      %ERROR_BUFFER_OVERFLOW = 111
      %ERROR_INVALID_PARAMETER = 87
      %ERROR_NO_DATA = 232
      '******************************************
      
      
      '****** DNS/MX Lookup Parameters *******
      DECLARE FUNCTION GetMXName(dnsReply() AS BYTE, iNdx AS INTEGER, _ 
                       iAnCount AS INTEGER) AS STRING
      DECLARE FUNCTION MX_Query(MailDomain AS STRING, DNSServer AS STRING) AS STRING
      DECLARE SUB ParseName(dnsReply() AS BYTE, iNdx AS INTEGER, sName AS STRING)
      
      %WINAPI               = 1
      %WSABASEERR           = 10000
      %WSAEINTR             = 10004
      %WSAEBADF             = 10009
      %WSAEACCES            = 10013
      %WSAEFAULT            = 10014
      %WSAEINVAL            = 10022
      %WSAEMFILE            = 10024
      %WSAEWOULDBLOCK       = 10035
      %WSAEINPROGRESS       = 10036
      %WSAEALREADY          = 10037
      %WSAENOTSOCK          = 10038
      %WSAEDESTADDRREQ      = 10039
      %WSAEMSGSIZE          = 10040
      %WSAEPROTOTYPE        = 10041
      %WSAENOPROTOOPT       = 10042
      %WSAEPROTONOSUPPORT   = 10043
      %WSAESOCKTNOSUPPORT   = 10044
      %WSAEOPNOTSUPP        = 10045
      %WSAEPFNOSUPPORT      = 10046
      %WSAEAFNOSUPPORT      = 10047
      %WSAEADDRINUSE        = 10048
      %WSAEADDRNOTAVAIL     = 10049
      %WSAENETDOWN          = 10050
      %WSAENETUNREACH       = 10051
      %WSAENETRESET         = 10052
      %WSAECONNABORTED      = 10053
      %WSAECONNRESET        = 10054
      %WSAENOBUFS           = 10055
      %WSAEISCONN           = 10056
      %WSAENOTCONN          = 10057
      %WSAESHUTDOWN         = 10058
      %WSAETOOMANYREFS      = 10059
      %WSAETIMEDOUT         = 10060
      %WSAECONNREFUSED      = 10061
      %WSAELOOP             = 10062
      %WSAENAMETOOLONG      = 10063
      %WSAEHOSTDOWN         = 10064
      %WSAEHOSTUNREACH      = 10065
      %WSAENOTEMPTY         = 10066
      %WSAEPROCLIM          = 10067
      %WSAEUSERS            = 10068
      %WSAEDQUOT            = 10069
      %WSAESTALE            = 10070
      %WSAEREMOTE           = 10071
      %WSASYSNOTREADY       = 10091
      %WSAVERNOTSUPPORTED   = 10092
      %WSANOTINITIALISED    = 10093
      %WSAEDISCON           = 10101
      %WSAHOST_NOT_FOUND    = 11001
      %WSATRY_AGAIN         = 11002
      %WSANO_RECOVERY       = 11003
      %WSANO_DATA           = 11004
      %CLASS_IN             = 1       'Internet class
      %QUERY_A              = 1       'Host Address
      %QUERY_NS             = 2       'Authoritative Name Server
      %QUERY_MD             = 3       'Mail destination (obsolete)
      %QUERY_MF             = 4       'Mail Forwarder (Obsolete)
      %QUERY_CNAME          = 5       'Canonical name of an alias
      %QUERY_SOA            = 6       'Start Zone of Authority
      %QUERY_MB             = 7       'Mailbox Domain Name
      %QUERY_MG             = 8       'Mail Group Member
      %QUERY_MR             = 9       'Mail Rename Domain Name
      %QUERY_NULL           = 10      'Null resource record
      %QUERY_WKS            = 11      'Well Known Service description
      %QUERY_PTR            = 12      'Domain Name Pointer
      %QUERY_HINFO          = 13      'Host Information
      %QUERY_MINFO          = 14      'Mailbox or Mail List information
      %QUERY_MX             = 15      'Mail Exchange
      %QUERY_TXT            = 16      'Text
      %QUERY_RP             = 17      'Responsible Person
      %QUERY_AFSDB          = 18      'AFS Data Base Location
      %QUERY_X25            = 19      'X.25 PSDN Address
      %QUERY_ISDN           = 20      'ISDN Address
      %QUERY_RT             = 21      'Route Through
      %QUERY_NSAP           = 22      'NSAP Style A Record
      %QUERY_NSAP_PTR       = 23      'NSAP Style A Record pointer
      %QUERY_SIG            = 24      'Security Signature
      %QUERY_KEY            = 25      'Security Key
      %QUERY_PX             = 26      'X.400 Mail Mapping Information
      %QUERY_GPOS           = 27      'Geographical Position
      %QUERY_AAAA           = 28      'IP6 Address
      %QUERY_LOC            = 29      'Location Information
      %QUERY_NXT            = 30      'Next Domain
      %QUERY_EID            = 31      'Endpoint Identifier
      %QUERY_NIMLOC         = 32      'Nimrod Locator
      %QUERY_SRV            = 33      'Server Selection
      %QUERY_IXFR           = 251     'Incremental Transfer
      %QUERY_AXFR           = 252     'Entire zone transfer
      %QUERY_MAILB          = 253     'Mailbox-related RRs (MB, MG, or MR)
      %QUERY_MAILA          = 254     'Mail Agent (obsolete)
      %QUERY_ALL            = 255     'All Records
      %ERROR_NOERROR        = 0        ' No Error
      %ERROR_FORMAT         = 1        ' Format Error
      %ERROR_SERVFAIL       = 2        ' Server Failure
      %ERROR_NXDOMAIN       = 3        ' Non-Existent Domain
      %ERROR_NOTIMP         = 4        ' Not Implemented
      %ERROR_REFUSED        = 5        ' Query Refused
      %ERROR_YXDOMAIN       = 6        ' Name Exists when it should not
      %ERROR_YXRRSET        = 7        ' RR Set Exists when it should not
      %ERROR_NXRRSET        = 8        ' RR Set that should exists does not
      %ERROR_NOTAUTH        = 9        ' Server Not Authoritative for Zone
      %DNS_FLAG_QR          = &H8000?
      %DNS_FLAG_AA          = &H0800?
      %DNS_FLAG_TC          = &H0400?
      %DNS_RFLAG            = &H0100?
      %DNS_FLAG_RA          = &H0100?
      %DNS_RCODE_MASK       = &H000F?
      %DNS_OPCODE_MASK      = &H7800?
      %PACKETSZ             = 512' maximum packet size
      %MAXDNAME             = 1025 ' maximum presentation domain name
      %MAXCDNAME            = 255' maximum compressed domain name
      %MAXLABEL             = 63 ' maximum length OF domain label
      %HFIXEDSZ             = 12 ' bytes OF fixed data in header
      %QFIXEDSZ             = 4  ' bytes OF fixed data in query
      %RRFIXEDSZ            = 10 ' bytes OF fixed data in r record
      %INADDRSZ             = 4  ' IPv4
      %IN6ADDRSZ            = 16 ' IPv6 T_AAAA
      %MAXALIASES           = 35
      %MAXADDRS             = 35
      %MAXDOMAINS           = 35
      %MAXSERVERS           = 10
      'User returned error codes
      %ERROR_USER_NOERROR   =  0        ' No Error or empty query
      %ERROR_USER_FORMAT    = -9        ' Format Error
      %ERROR_USER_SERVFAIL  = -8        ' Server Failure
      %ERROR_USER_NXDOMAIN  = -7        ' Non-Existent Domain
      %ERROR_USER_NOTIMP    = -6        ' Not Implemented
      %ERROR_USER_REFUSED   = -5        ' Query Refused
      %ERROR_USER_YXDOMAIN  = -4        ' Name Exists when it should not
      %ERROR_USER_YXRRSET   = -3        ' RR Set Exists when it should not
      %ERROR_USER_NXRRSET   = -2        ' RR Set that should exists does not
      %ERROR_USER_NOTAUTH   = -1        ' Server Not Authoritative for Zone
      %ERROR_USER_NOQUERY   = -10       ' Query length passed is null or not a query
      
      
      TYPE MX_RECORD
          wPreference AS WORD
          szName AS ASCIIZ * 256
          szDomain AS ASCIIZ * 256
          szIp AS ASCIIZ * 16
      END TYPE
      
      TYPE NS_RECORD
          szName AS ASCIIZ * 256
          szDomain AS ASCIIZ * 256
      END TYPE
      
      TYPE QR_REC
          wQueryType AS WORD
          wQueryClass AS WORD
      END TYPE
      
      TYPE DNS_HEADER
          wId AS WORD
          wBitFields AS WORD
          wQCount AS WORD
          wARCount AS WORD
          wAAcount AS WORD
          wAAARCount AS WORD
      END TYPE
      
      TYPE DNS_RR_HEADER
          wType AS WORD  'Type code (A, MX, etc)
          wClass AS WORD 'Class code (IN for internet, etc)
          dwTTL AS DWORD   'Time to live
          wLength AS WORD'Len of wRdata field (in octects)
          dwRData AS DWORD 'Field name address (variable length)
      END TYPE
      
      TYPE T_QUERY
          tHeader AS DNS_HEADER
          tQrRec AS QR_REC
      END TYPE
      
      UNION U_PARSE_PTR
          bPtr AS BYTE PTR
          wPtr AS WORD PTR
          lPtr AS LONG PTR
          dwPtr AS DWORD PTR
      END UNION
      
      TYPE sockaddralt
          sin_family AS INTEGER
          sin_port AS INTEGER
          sin_addr AS LONG
          sin_zero AS STRING * 8
      END TYPE
      
      
      FUNCTION DefaultDNS() AS STRING
          LOCAL lngFixedInfoNeeded    AS LONG
          LOCAL bytFixedInfoBuffer()  AS BYTE
          LOCAL udtFixedInfo          AS FIXED_INFO
          LOCAL Buffer                AS IP_ADDR_STRING
          LOCAL strFixedInfoScopeId   AS STRING
          LOCAL lngWin32apiResultCode AS LONG
          LOCAL ProcessID             AS LONG
          LOCAL hLib                  AS LONG
          LOCAL ProcAddr              AS DWORD
          LOCAL pAddrStr              AS LONG
          LOCAL I                     AS INTEGER
          
          ' Here we see if iphlpapi.dll is on this machine
          ProcessID = GETCURRENTPROCESSID()
          hLib = LOADLIBRARY("iphlpapi.dll")
          IF ISFALSE hLib THEN
             MSGBOX "Sorry, iphlpapi.dll not supported on this machine. Terminating program."
             EXIT FUNCTION
          END IF
      
          'Retrieve FUNCTION address
          ProcAddr = GETPROCADDRESS(hLib,"GetNetworkParams")
          IF ProcAddr = %NULL THEN
             MSGBOX "Sorry, GetNetworkParams not supported on this machine. Terminating program."
             EXIT FUNCTION
          END IF
          lngFixedInfoNeeded=LEN(udtFixedInfo)
      
          'Call pointer to the function using function prototype
          CALL DWORD ProcAddr USING GetNetworkParams(BYVAL %NULL ,lngFixedInfoNeeded) TO lngWin32apiResultCode
          IF lngWin32apiResultCode <> 0 THEN
             SELECT CASE lngWin32apiResultCode
                  CASE %ERROR_BUFFER_OVERFLOW
                      ' Right-size bytFixedInfoBuffer
                      REDIM bytFixedInfoBuffer (lngFixedInfoNeeded)
                  CASE %ERROR_INVALID_PARAMETER
                      MSGBOX "Invalid parameter Error."
                      EXIT FUNCTION
                  CASE %ERROR_NO_DATA
                      MSGBOX "No data Error."
                      EXIT FUNCTION
                  CASE %ERROR_NOT_SUPPORTED
                      MSGBOX "Not Supported Error."
                      EXIT FUNCTION
                  CASE ELSE
                      MSGBOX STR$(lngWin32apiResultCode) + " error code returned."
                      EXIT FUNCTION
             END SELECT
          ELSE
             ' Something is horribly wrong as we should not get 0
             ' for the first pass through. Get outta here!
             MSGBOX "Unexpected result. Terminating program."
             EXIT FUNCTION
          END IF
          'Call pointer to the function using function prototype
          CALL DWORD ProcAddr USING GetNetworkParams(bytFixedInfoBuffer(0) , lngFixedInfoNeeded) TO lngWin32apiResultCode
          MOVEMEMORY bva(udtFixedInfo), bva(bytFixedInfoBuffer(0)), LEN(udtFixedInfo)
          
          FUNCTION = udtFixedInfo.DnsServerList.IpAddress
      END FUNCTION
      
      
      DNS$ = DefaultDNS
      
      '---Look up MX record, check all 3 DNS services as needed---
      FOR iLOOP& = 1 TO 3
          x$ = "NS" & FORMAT$(iLoop&)
          MX$ = TRIM$(MX_Query(Domain,DNS$))
          '---Sort any MX records returned by priority value---
          ARRAY SORT AllMXs(), FROM 1 TO 3
          iCount& = UBOUND(AllMXs())
          IF iCount& > 0 THEN EXIT FOR   'No need to test other DNS servers
          EZ_DOEVENTS 255
      NEXT
      
      MSGBOX FORMAT$(iCount&) & " MX records found"
      
      FUNCTION MX_Query(MailDomain AS STRING, DNSServer AS STRING) AS STRING
          DIM StartupData AS WSAdata
          DIM SocketBuffer AS sockaddralt
          DIM IpAddr AS LONG
          DIM iRC AS INTEGER
          DIM dnsHead AS DNS_HEADER
          DIM iSock AS INTEGER
          DIM MailDomainTemp AS STRING
          DIM dnsQuery() AS BYTE
          DIM sQName AS STRING
          DIM dnsQueryNdx AS INTEGER
          DIM iTemp AS INTEGER
          DIM iNdx AS INTEGER
          DIM dnsReply(2048) AS BYTE
          DIM iAnCount AS INTEGER
          DIM I AS INTEGER
          DIM TempBuffer AS ASCIIZ*2049
          
          MailDomainTemp=TRIM$(MailDomain)
          ' Initialize the Winsocket
          iRC = WSAStartup(&H101, StartupData)
          FUNCTION=""
          ' Create a socket
          iSock = socket(%AF_INET, %SOCK_DGRAM, 0)
          IF iSock = %SOCKET_ERROR THEN EXIT FUNCTION
          HOST ADDR DNSServer TO IpAddr
          IF IpAddr = -1 THEN EXIT FUNCTION
          ' Setup the connnection parameters
          SocketBuffer.sin_family = %AF_INET
          SocketBuffer.sin_port = htons(53)
          SocketBuffer.sin_addr = IpAddr
          SocketBuffer.sin_zero = STRING$(8, 0)
          ' Set the DNS parameters
          dnsHead.wId = htons(&H11DF)            ' dnsHead.qryID = htons(&H11DF)
          dnsHead.wBitFields = 1                 ' dnsHead.options = DNS_RECURSION
          dnsHead.wQCount = htons(1)             ' dnsHead.qdcount = htons(1)
          dnsHead.wARCount = 0                   ' dnsHead.ancount = 0
          dnsHead.wAAcount = 0                   ' dnsHead.nscount = 0
          dnsHead.wAAARCount = 0                 ' dnsHead.arcount = 0 
          dnsQueryNdx = 0
          REDIM dnsQuery(4000)
          MOVEMEMORY BYVAL VARPTR(dnsQuery(dnsQueryNdx)), BYVAL VARPTR(dnsHead), 12
          dnsQueryNdx = dnsQueryNdx + 12
          sQName = MakeQName(MailDomainTemp)
          iNdx = 0
          WHILE (iNdx < LEN(sQName))
              dnsQuery(dnsQueryNdx + iNdx) = ASC(MID$(sQName, iNdx + 1, 1))
              iNdx = iNdx + 1
          WEND
          dnsQueryNdx = dnsQueryNdx + LEN(sQName)
          dnsQuery(dnsQueryNdx) = &H0
          dnsQueryNdx = dnsQueryNdx + 1
          iTemp = htons(15)
          MOVEMEMORY bva(dnsQuery(dnsQueryNdx)), bva(iTemp), LEN(iTemp)
          dnsQueryNdx = dnsQueryNdx + LEN(iTemp)
          iTemp = htons(1)
          MOVEMEMORY bva(dnsQuery(dnsQueryNdx)), bva(iTemp), LEN(iTemp)
          dnsQueryNdx = dnsQueryNdx + LEN(iTemp)
          REDIM PRESERVE dnsQuery(dnsQueryNdx - 1)
          iRC = sendto(iSock, dnsQuery(0), dnsQueryNdx + 1, 0, SocketBuffer, LEN(SocketBuffer))
          IF (iRC = %SOCKET_ERROR) THEN
              EXIT FUNCTION
          END IF
          iRC = recvfrom(iSock, dnsReply(0), 2048, 0, SocketBuffer, LEN(SocketBuffer))
          IF (iRC = %SOCKET_ERROR) THEN
              EXIT FUNCTION
          END IF
          MOVEMEMORY bva(iAnCount), bva(dnsReply(6)), 2
          iAnCount = ntohs(iAnCount)
          FOR I= 0 TO 2048
              TempBuffer=TempBuffer+CHR$(dnsReply(I))
          NEXT I
          FUNCTION = GetMXName(dnsReply(), 12, iAnCount)
      END FUNCTION
      That should be enough to at least get you in the right area. Let me know if you need more explaination.
      Software makes Hardware Happen

      Comment


      • #4
        Originally posted by Kev Peel View Post
        I'm having trouble finding a reliable third-party SMTP server that doesn't cost a bomb [...]
        I'm using CMailServer whenever I'm in need of an easy to set up mailserver (i.e. for testing/demonstration purposes).

        It's free for 5 users (5 mail accounts), which should be enough for your needs (just sending out emails, if I read your posting right).

        Comment


        • #5
          Thanks Joe & Knuth,

          Joe that code looks especially useful I'll also take a look at CMailServer.

          The mailing lists may get very large in time as I will be adding the opt-in to my product pages and downloads sometimes hit >100 a day, so it would also be nice to detect and remove bad addresses from the lists and build a more "interactive" user-base. Mailings would only be sent when the product the list is related to is updated.

          Thanks again!
          kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

          Comment


          • #6
            All the code for a full email server is in this forum. I know, I wrote it. The prototype uses a mysql backend but is a simple matter to use the alternate backend Sqlite the libraries for both have been posted. If you have any question please feel free

            Comment


            • #7
              The &quot;hard&quot; part was getting the MX records...

              Florent Heyworth posted source code for MX record lookups a long time ago

              http://www.powerbasic.com/support/pb...ad.php?t=24950
              Michael Burns

              Comment

              Working...
              X