Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Python alternative to getadsmtp.pl; Runs faster and performs the same AD query for valid SMTP recipients. Credits noted in the comments.
#!/usr/bin/env python
#Credits: Marc Smith, http://marcitland.blogspot.com/2011/02/python-active-directory-linux.html
# DarkPixel, https://github.com/darkpixel/scripts/blob/master/getadsmtp.py
# JR, http://liveaverage.com
import sys, ldap, argparse
import ldap.modlist as modlist
from ldap.controls import SimplePagedResultsControl
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description="Retrieve e-mail addresses from an LDAP server in postfix format")
parser.add_argument('-c', '--connect', required=True, action='store', help='The host to connect to (AD/Exchange Server)')
parser.add_argument('-r', '--port', action='store', help='Port to use for connecting, defaults to 636')
parser.add_argument('-u', '--user', action='store', required=True, help='Username to use (either cn=blah,dc=cust,dc=local or blah@cust.local format)')
parser.add_argument('-p', '--password', action='store', required=True, help='Password')
parser.add_argument('-o', '--ou', action='store', required=True, help='Org Unit (Base DN) to export from')
arg = parser.parse_args()
LDAP_SERVER = 'ldaps://%s:%s' %(arg.connect, arg.port or '3269')
BIND_DN = arg.user
BIND_PASS = arg.password
USER_FILTER = "(& (mailnickname=*) (| (objectClass=publicFolder)(&(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))(objectCategory=person)(objectCategory=group)(objectClass=msExchDynamicDistributionList) ) (!(objectClass=contact)) )"
USER_BASE = arg.ou
PAGE_SIZE = 500
# LDAP connection
try:
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ldap_connection = ldap.initialize(LDAP_SERVER)
ldap_connection.set_option(ldap.OPT_REFERRALS, 0)
ldap_connection.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
ldap_connection.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND)
ldap_connection.set_option( ldap.OPT_X_TLS_DEMAND, True )
ldap_connection.set_option( ldap.OPT_DEBUG_LEVEL, 255 )
ldap_connection.simple_bind_s(BIND_DN, BIND_PASS)
except ldap.LDAPError, e:
sys.stderr.write('Error connecting to LDAP server: ' + str(e) + '\n')
sys.exit(1)
# Lookup usernames from LDAP via paged search
paged_results_control = SimplePagedResultsControl(
ldap.LDAP_CONTROL_PAGE_OID, True, (PAGE_SIZE, ''))
accounts = []
pages = 0
while True:
serverctrls = [paged_results_control]
try:
msgid = ldap_connection.search_ext(USER_BASE,
ldap.SCOPE_SUBTREE,
USER_FILTER,
attrlist=['proxyAddresses'],
serverctrls=serverctrls)
except ldap.LDAPError, e:
sys.stderr.write('Error performing user paged search: ' +
str(e) + '\n')
sys.exit(1)
try:
unused_code, results, unused_msgid, serverctrls = \
ldap_connection.result3(msgid)
except ldap.LDAPError, e:
sys.stderr.write('Error getting user paged search results: ' +
str(e) + '\n')
sys.exit(1)
for result in results:
pages += 1
accounts.append(result)
cookie = None
for serverctrl in serverctrls:
if serverctrl.controlType == ldap.LDAP_CONTROL_PAGE_OID:
unused_est, cookie = serverctrl.controlValue
if cookie:
paged_results_control.controlValue = (PAGE_SIZE, cookie)
break
if not cookie:
break
# LDAP unbind
ldap_connection.unbind_s()
#Address Counter:
ac = 0
for entry in accounts:
#DEBUG: Convert entry to string and print:
#print "%s" % str(entry)
if hasattr(entry[1], 'has_key') and entry[1].has_key('proxyAddresses'):
addresses = entry[1]['proxyAddresses']
for addr in addresses:
if 'smtp:' in addr.lower():
ac += 1
print "%s OK" % (addr.lower().split('smtp:')[1])
#DEBUG: Convert addr to string and print:
#print "%s" % str(addr)
print "# %d results" % len(accounts)
#print "%d proxyAddresses" % ac
@liveaverage

This comment has been minimized.

Copy link
Owner Author

@liveaverage liveaverage commented Feb 27, 2013

Revised the original script's LDAP filter to include Public Folders when scraping Exchange for valid proxyAddresses. I found out the hard way it wasn't adding PFs with associated mail addresses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment