Fix all warnings in source3 with gcc4.3.
[ira/wip.git] / source3 / utils / net_ads.c
index 141031dacb29c19b732e5ca6dfe56b4a8e89a3d5..766f3216f0b9c200c9c0ce246d2f75a34dc21caa 100644 (file)
 
 #include "includes.h"
 #include "utils/net.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
 
 #ifdef HAVE_ADS
 
-int net_ads_usage(int argc, const char **argv)
-{
-       d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
-       d_printf("    Join the local machine to a ADS realm\n");
-       d_printf("leave\n");
-       d_printf("    Remove the local machine from a ADS realm\n");
-       d_printf("testjoin\n");
-       d_printf("    Validates the machine account in the domain\n");
-       d_printf("user\n");
-       d_printf("    List, add, or delete users in the realm\n");
-       d_printf("group\n");
-       d_printf("    List, add, or delete groups in the realm\n");
-       d_printf("info\n");
-       d_printf("    Displays details regarding a specific AD server\n");
-       d_printf("status\n");
-       d_printf("    Display details regarding the machine's account in AD\n");
-       d_printf("lookup\n");
-       d_printf("    Performs CLDAP query of AD domain controllers\n");
-       d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
-       d_printf("    Change a user's password using an admin account\n");
-       d_printf("    (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
-       d_printf("changetrustpw\n");
-       d_printf("    Change the trust account password of this machine in the AD tree\n");
-       d_printf("printer [info | publish | remove] <printername> <servername>\n");
-       d_printf("    Lookup, add, or remove directory entry for a printer\n");
-       d_printf("{search,dn,sid}\n");
-       d_printf("    Issue LDAP search queries using a general filter, by DN, or by SID\n");
-       d_printf("keytab\n");
-       d_printf("    Manage a local keytab file based on the machine account in AD\n");
-       d_printf("dns\n");
-       d_printf("    Issue a dynamic DNS update request the server's hostname\n");
-       d_printf("    (using the machine credentials)\n");
-
-       return -1;
-}
-
 /* when we do not have sufficient input parameters to contact a remote domain
  * we always fall back to our own realm - Guenther*/
 
-static const char *assume_own_realm(void)
+static const char *assume_own_realm(struct net_context *c)
 {
-       if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
+       if (!c->opt_host && strequal(lp_workgroup(), c->opt_target_workgroup)) {
                return lp_realm();
        }
 
@@ -76,14 +41,13 @@ static const char *assume_own_realm(void)
 /*
   do a cldap netlogon query
 */
-static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
+static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
 {
        char addr[INET6_ADDRSTRLEN];
-       struct cldap_netlogon_reply reply;
-       struct GUID tmp_guid;
+       struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
 
        print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
-       if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
+       if ( !ads_cldap_netlogon_5(talloc_tos(), addr, ads->server.realm, &reply ) ) {
                d_fprintf(stderr, "CLDAP query failed!\n");
                return -1;
        }
@@ -92,20 +56,19 @@ static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
                addr);
 
        d_printf("Response Type: ");
-       switch (reply.type) {
-       case SAMLOGON_AD_UNK_R:
-               d_printf("SAMLOGON\n");
+       switch (reply.command) {
+       case LOGON_SAM_LOGON_USER_UNKNOWN_EX:
+               d_printf("LOGON_SAM_LOGON_USER_UNKNOWN_EX\n");
                break;
-       case SAMLOGON_AD_R:
-               d_printf("SAMLOGON_USER\n");
+       case LOGON_SAM_LOGON_RESPONSE_EX:
+               d_printf("LOGON_SAM_LOGON_RESPONSE_EX\n");
                break;
        default:
-               d_printf("0x%x\n", reply.type);
+               d_printf("0x%x\n", reply.command);
                break;
        }
 
-       smb_uuid_unpack(reply.guid, &tmp_guid);
-       d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), tmp_guid));
+       d_printf("GUID: %s\n", GUID_string(talloc_tos(), &reply.domain_uuid));
 
        d_printf("Flags:\n"
                 "\tIs a PDC:                                   %s\n"
@@ -117,32 +80,36 @@ static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
                 "\tIs the closest DC:                          %s\n"
                 "\tIs writable:                                %s\n"
                 "\tHas a hardware clock:                       %s\n"
-                "\tIs a non-domain NC serviced by LDAP server: %s\n",
-                (reply.flags & ADS_PDC) ? "yes" : "no",
-                (reply.flags & ADS_GC) ? "yes" : "no",
-                (reply.flags & ADS_LDAP) ? "yes" : "no",
-                (reply.flags & ADS_DS) ? "yes" : "no",
-                (reply.flags & ADS_KDC) ? "yes" : "no",
-                (reply.flags & ADS_TIMESERV) ? "yes" : "no",
-                (reply.flags & ADS_CLOSEST) ? "yes" : "no",
-                (reply.flags & ADS_WRITABLE) ? "yes" : "no",
-                (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
-                (reply.flags & ADS_NDNC) ? "yes" : "no");
+                "\tIs a non-domain NC serviced by LDAP server: %s\n"
+                "\tIs NT6 DC that has some secrets:            %s\n"
+                "\tIs NT6 DC that has all secrets:             %s\n",
+                (reply.server_type & NBT_SERVER_PDC) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_GC) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_LDAP) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_DS) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_KDC) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_TIMESERV) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_CLOSEST) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_WRITABLE) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_NDNC) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6) ? "yes" : "no",
+                (reply.server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6) ? "yes" : "no");
+
 
        printf("Forest:\t\t\t%s\n", reply.forest);
-       printf("Domain:\t\t\t%s\n", reply.domain);
-       printf("Domain Controller:\t%s\n", reply.hostname);
+       printf("Domain:\t\t\t%s\n", reply.dns_domain);
+       printf("Domain Controller:\t%s\n", reply.pdc_dns_name);
 
-       printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
-       printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
+       printf("Pre-Win2k Domain:\t%s\n", reply.domain);
+       printf("Pre-Win2k Hostname:\t%s\n", reply.pdc_name);
 
-       if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
        if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
 
-       printf("Server Site Name :\t\t%s\n", reply.server_site_name);
-       printf("Client Site Name :\t\t%s\n", reply.client_site_name);
+       printf("Server Site Name :\t\t%s\n", reply.server_site);
+       printf("Client Site Name :\t\t%s\n", reply.client_site);
 
-       d_printf("NT Version: %d\n", reply.version);
+       d_printf("NT Version: %d\n", reply.nt_version);
        d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
        d_printf("LM20 Token: %.2x\n", reply.lm20_token);
 
@@ -153,31 +120,46 @@ static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
   this implements the CLDAP based netlogon lookup requests
   for finding the domain controller of a ADS domain
 */
-static int net_ads_lookup(int argc, const char **argv)
+static int net_ads_lookup(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
 
-       if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads lookup\n"
+                        "    Find the ADS DC using CLDAP lookup.\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
                d_fprintf(stderr, "Didn't find the cldap server!\n");
                return -1;
        }
 
        if (!ads->config.realm) {
-               ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
+               ads->config.realm = CONST_DISCARD(char *, c->opt_target_workgroup);
                ads->ldap.port = 389;
        }
 
-       return net_ads_cldap_netlogon(ads);
+       return net_ads_cldap_netlogon(c, ads);
 }
 
 
 
-static int net_ads_info(int argc, const char **argv)
+static int net_ads_info(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        char addr[INET6_ADDRSTRLEN];
 
-       if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads info\n"
+                        "    Display information about an Active Directory "
+                        "server.\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
                d_fprintf(stderr, "Didn't find the ldap server!\n");
                return -1;
        }
@@ -201,7 +183,8 @@ static int net_ads_info(int argc, const char **argv)
        d_printf("Realm: %s\n", ads->config.realm);
        d_printf("Bind Path: %s\n", ads->config.bind_path);
        d_printf("LDAP port: %d\n", ads->ldap.port);
-       d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
+       d_printf("Server time: %s\n", 
+                        http_timestring(talloc_tos(), ads->config.current_time));
 
        d_printf("KDC server: %s\n", ads->auth.kdc_server );
        d_printf("Server time offset: %d\n", ads->auth.time_offset );
@@ -215,15 +198,16 @@ static void use_in_memory_ccache(void) {
        setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
 }
 
-static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
+static ADS_STATUS ads_startup_int(struct net_context *c, bool only_own_domain,
+                                 uint32 auth_flags, ADS_STRUCT **ads_ret)
 {
        ADS_STRUCT *ads = NULL;
        ADS_STATUS status;
-       bool need_password = False;
-       bool second_time = False;
+       bool need_password = false;
+       bool second_time = false;
        char *cp;
        const char *realm = NULL;
-       bool tried_closest_dc = False;
+       bool tried_closest_dc = false;
 
        /* lp_realm() should be handled by a command line param,
           However, the join requires that realm be set in smb.conf
@@ -236,37 +220,37 @@ retry_connect:
        if (only_own_domain) {
                realm = lp_realm();
        } else {
-               realm = assume_own_realm();
+               realm = assume_own_realm(c);
        }
 
-       ads = ads_init(realm, opt_target_workgroup, opt_host);
+       ads = ads_init(realm, c->opt_target_workgroup, c->opt_host);
 
-       if (!opt_user_name) {
-               opt_user_name = "administrator";
+       if (!c->opt_user_name) {
+               c->opt_user_name = "administrator";
        }
 
-       if (opt_user_specified) {
-               need_password = True;
+       if (c->opt_user_specified) {
+               need_password = true;
        }
 
 retry:
-       if (!opt_password && need_password && !opt_machine_pass) {
-               opt_password = net_prompt_pass(opt_user_name);
-               if (!opt_password) {
+       if (!c->opt_password && need_password && !c->opt_machine_pass) {
+               c->opt_password = net_prompt_pass(c, c->opt_user_name);
+               if (!c->opt_password) {
                        ads_destroy(&ads);
                        return ADS_ERROR(LDAP_NO_MEMORY);
                }
        }
 
-       if (opt_password) {
+       if (c->opt_password) {
                use_in_memory_ccache();
                SAFE_FREE(ads->auth.password);
-               ads->auth.password = smb_xstrdup(opt_password);
+               ads->auth.password = smb_xstrdup(c->opt_password);
        }
 
        ads->auth.flags |= auth_flags;
        SAFE_FREE(ads->auth.user_name);
-       ads->auth.user_name = smb_xstrdup(opt_user_name);
+       ads->auth.user_name = smb_xstrdup(c->opt_user_name);
 
        /*
         * If the username is of the form "name@realm",
@@ -292,8 +276,8 @@ retry:
                }
 
                if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
-                       need_password = True;
-                       second_time = True;
+                       need_password = true;
+                       second_time = true;
                        goto retry;
                } else {
                        ads_destroy(&ads);
@@ -305,11 +289,11 @@ retry:
         * This is done by reconnecting to ADS because only the first call to
         * ads_connect will give us our own sitename */
 
-       if ((only_own_domain || !opt_host) && !tried_closest_dc) {
+       if ((only_own_domain || !c->opt_host) && !tried_closest_dc) {
 
-               tried_closest_dc = True; /* avoid loop */
+               tried_closest_dc = true; /* avoid loop */
 
-               if (!ads->config.tried_closest_dc) {
+               if (!ads_closest_dc(ads)) {
 
                        namecache_delete(ads->server.realm, 0x1C);
                        namecache_delete(ads->server.workgroup, 0x1C);
@@ -325,14 +309,14 @@ retry:
        return status;
 }
 
-ADS_STATUS ads_startup(bool only_own_domain, ADS_STRUCT **ads)
+ADS_STATUS ads_startup(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
 {
-       return ads_startup_int(only_own_domain, 0, ads);
+       return ads_startup_int(c, only_own_domain, 0, ads);
 }
 
-ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
+ADS_STATUS ads_startup_nobind(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
 {
-       return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
+       return ads_startup_int(c, only_own_domain, ADS_AUTH_NO_BIND, ads);
 }
 
 /*
@@ -360,42 +344,49 @@ static int net_ads_check_int(const char *realm, const char *workgroup, const cha
        return 0;
 }
 
-int net_ads_check_our_domain(void)
+int net_ads_check_our_domain(struct net_context *c)
 {
        return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
 }
 
-int net_ads_check(void)
+int net_ads_check(struct net_context *c)
 {
-       return net_ads_check_int(NULL, opt_workgroup, opt_host);
+       return net_ads_check_int(NULL, c->opt_workgroup, c->opt_host);
 }
 
 /*
    determine the netbios workgroup name for a domain
  */
-static int net_ads_workgroup(int argc, const char **argv)
+static int net_ads_workgroup(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        char addr[INET6_ADDRSTRLEN];
-       struct cldap_netlogon_reply reply;
+       struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
+
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads workgroup\n"
+                        "    Print the workgroup name\n");
+               return 0;
+       }
 
-       if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
                d_fprintf(stderr, "Didn't find the cldap server!\n");
                return -1;
        }
 
        if (!ads->config.realm) {
-               ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
+               ads->config.realm = CONST_DISCARD(char *, c->opt_target_workgroup);
                ads->ldap.port = 389;
        }
 
        print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
-       if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
+       if ( !ads_cldap_netlogon_5(talloc_tos(), addr, ads->server.realm, &reply ) ) {
                d_fprintf(stderr, "CLDAP query failed!\n");
                return -1;
        }
 
-       d_printf("Workgroup: %s\n", reply.netbios_domain);
+       d_printf("Workgroup: %s\n", reply.domain);
 
        ads_destroy(&ads);
 
@@ -420,24 +411,24 @@ static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *d
                }
                SAFE_FREE(disp_fields[0]);
                SAFE_FREE(disp_fields[1]);
-               return True;
+               return true;
        }
        if (!values) /* must be new field, indicate string field */
-               return True;
+               return true;
        if (StrCaseCmp(field, "sAMAccountName") == 0) {
                disp_fields[0] = SMB_STRDUP((char *) values[0]);
        }
        if (StrCaseCmp(field, "description") == 0)
                disp_fields[1] = SMB_STRDUP((char *) values[0]);
-       return True;
+       return true;
 }
 
-static int net_ads_user_usage(int argc, const char **argv)
+static int net_ads_user_usage(struct net_context *c, int argc, const char **argv)
 {
-       return net_help_user(argc, argv);
+       return net_user_usage(c, argc, argv);
 }
 
-static int ads_user_add(int argc, const char **argv)
+static int ads_user_add(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS status;
@@ -446,9 +437,10 @@ static int ads_user_add(int argc, const char **argv)
        int rc = -1;
        char *ou_str = NULL;
 
-       if (argc < 1) return net_ads_user_usage(argc, argv);
+       if (argc < 1 || c->display_usage)
+               return net_ads_user_usage(c, argc, argv);
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -464,13 +456,13 @@ static int ads_user_add(int argc, const char **argv)
                goto done;
        }
 
-       if (opt_container) {
-               ou_str = SMB_STRDUP(opt_container);
+       if (c->opt_container) {
+               ou_str = SMB_STRDUP(c->opt_container);
        } else {
                ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
        }
 
-       status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
+       status = ads_add_user_acct(ads, argv[0], ou_str, c->opt_comment);
 
        if (!ADS_ERR_OK(status)) {
                d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
@@ -486,10 +478,12 @@ static int ads_user_add(int argc, const char **argv)
        }
 
        /* try setting the password */
-       asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
+       if (asprintf(&upn, "%s@%s", argv[0], ads->config.realm) == -1) {
+               goto done;
+       }
        status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
                                       ads->auth.time_offset);
-       safe_free(upn);
+       SAFE_FREE(upn);
        if (ADS_ERR_OK(status)) {
                d_printf("User %s added\n", argv[0]);
                rc = 0;
@@ -515,7 +509,7 @@ static int ads_user_add(int argc, const char **argv)
        return rc;
 }
 
-static int ads_user_info(int argc, const char **argv)
+static int ads_user_info(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -525,8 +519,8 @@ static int ads_user_info(int argc, const char **argv)
        char **grouplist;
        char *escaped_user;
 
-       if (argc < 1) {
-               return net_ads_user_usage(argc, argv);
+       if (argc < 1 || c->display_usage) {
+               return net_ads_user_usage(c, argc, argv);
        }
 
        escaped_user = escape_ldap_string_alloc(argv[0]);
@@ -536,14 +530,17 @@ static int ads_user_info(int argc, const char **argv)
                return -1;
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                SAFE_FREE(escaped_user);
                return -1;
        }
 
-       asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
+       if (asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user) == -1) {
+               SAFE_FREE(escaped_user);
+               return -1;
+       }
        rc = ads_search(ads, &res, searchstring, attrs);
-       safe_free(searchstring);
+       SAFE_FREE(searchstring);
 
        if (!ADS_ERR_OK(rc)) {
                d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
@@ -572,7 +569,7 @@ static int ads_user_info(int argc, const char **argv)
        return 0;
 }
 
-static int ads_user_delete(int argc, const char **argv)
+static int ads_user_delete(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -580,10 +577,10 @@ static int ads_user_delete(int argc, const char **argv)
        char *userdn;
 
        if (argc < 1) {
-               return net_ads_user_usage(argc, argv);
+               return net_ads_user_usage(c, argc, argv);
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -609,13 +606,34 @@ static int ads_user_delete(int argc, const char **argv)
        return -1;
 }
 
-int net_ads_user(int argc, const char **argv)
+int net_ads_user(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"ADD", ads_user_add},
-               {"INFO", ads_user_info},
-               {"DELETE", ads_user_delete},
-               {NULL, NULL}
+               {
+                       "add",
+                       ads_user_add,
+                       NET_TRANSPORT_ADS,
+                       "Add an AD user",
+                       "net ads user add\n"
+                       "    Add an AD user"
+               },
+               {
+                       "info",
+                       ads_user_info,
+                       NET_TRANSPORT_ADS,
+                       "Display information about an AD user",
+                       "net ads user info\n"
+                       "    Display information about an AD user"
+               },
+               {
+                       "delete",
+                       ads_user_delete,
+                       NET_TRANSPORT_ADS,
+                       "Delete an AD user",
+                       "net ads user delete\n"
+                       "    Delete an AD user"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -624,33 +642,41 @@ int net_ads_user(int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
 
        if (argc == 0) {
-               if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+               if (c->display_usage) {
+                       d_printf("Usage:\n");
+                       d_printf("net ads user\n"
+                                "    List AD users\n");
+                       net_display_usage_from_functable(func);
+                       return 0;
+               }
+
+               if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                        return -1;
                }
 
-               if (opt_long_list_entries)
-                       d_printf("\nUser name             Comment"\
+               if (c->opt_long_list_entries)
+                       d_printf("\nUser name             Comment"
                                 "\n-----------------------------\n");
 
                rc = ads_do_search_all_fn(ads, ads->config.bind_path,
                                          LDAP_SCOPE_SUBTREE,
                                          "(objectCategory=user)",
-                                         opt_long_list_entries ? longattrs :
+                                         c->opt_long_list_entries ? longattrs :
                                          shortattrs, usergrp_display,
                                          disp_fields);
                ads_destroy(&ads);
                return ADS_ERR_OK(rc) ? 0 : -1;
        }
 
-       return net_run_function(argc, argv, func, net_ads_user_usage);
+       return net_run_function(c, argc, argv, "net ads user", func);
 }
 
-static int net_ads_group_usage(int argc, const char **argv)
+static int net_ads_group_usage(struct net_context *c, int argc, const char **argv)
 {
-       return net_help_group(argc, argv);
+       return net_group_usage(c, argc, argv);
 }
 
-static int ads_group_add(int argc, const char **argv)
+static int ads_group_add(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS status;
@@ -658,11 +684,11 @@ static int ads_group_add(int argc, const char **argv)
        int rc = -1;
        char *ou_str = NULL;
 
-       if (argc < 1) {
-               return net_ads_group_usage(argc, argv);
+       if (argc < 1 || c->display_usage) {
+               return net_ads_group_usage(c, argc, argv);
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -678,13 +704,13 @@ static int ads_group_add(int argc, const char **argv)
                goto done;
        }
 
-       if (opt_container) {
-               ou_str = SMB_STRDUP(opt_container);
+       if (c->opt_container) {
+               ou_str = SMB_STRDUP(c->opt_container);
        } else {
                ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
        }
 
-       status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
+       status = ads_add_group_acct(ads, argv[0], ou_str, c->opt_comment);
 
        if (ADS_ERR_OK(status)) {
                d_printf("Group %s added\n", argv[0]);
@@ -702,18 +728,18 @@ static int ads_group_add(int argc, const char **argv)
        return rc;
 }
 
-static int ads_group_delete(int argc, const char **argv)
+static int ads_group_delete(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        LDAPMessage *res = NULL;
        char *groupdn;
 
-       if (argc < 1) {
-               return net_ads_group_usage(argc, argv);
+       if (argc < 1 || c->display_usage) {
+               return net_ads_group_usage(c, argc, argv);
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -739,12 +765,26 @@ static int ads_group_delete(int argc, const char **argv)
        return -1;
 }
 
-int net_ads_group(int argc, const char **argv)
+int net_ads_group(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"ADD", ads_group_add},
-               {"DELETE", ads_group_delete},
-               {NULL, NULL}
+               {
+                       "add",
+                       ads_group_add,
+                       NET_TRANSPORT_ADS,
+                       "Add an AD group",
+                       "net ads group add\n"
+                       "    Add an AD group"
+               },
+               {
+                       "delete",
+                       ads_group_delete,
+                       NET_TRANSPORT_ADS,
+                       "Delete an AD group",
+                       "net ads group delete\n"
+                       "    Delete an AD group"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -753,33 +793,48 @@ int net_ads_group(int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
 
        if (argc == 0) {
-               if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+               if (c->display_usage) {
+                       d_printf("Usage:\n");
+                       d_printf("net ads group\n"
+                                "    List AD groups\n");
+                       net_display_usage_from_functable(func);
+                       return 0;
+               }
+
+               if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                        return -1;
                }
 
-               if (opt_long_list_entries)
-                       d_printf("\nGroup name            Comment"\
+               if (c->opt_long_list_entries)
+                       d_printf("\nGroup name            Comment"
                                 "\n-----------------------------\n");
                rc = ads_do_search_all_fn(ads, ads->config.bind_path,
                                          LDAP_SCOPE_SUBTREE,
                                          "(objectCategory=group)",
-                                         opt_long_list_entries ? longattrs :
+                                         c->opt_long_list_entries ? longattrs :
                                          shortattrs, usergrp_display,
                                          disp_fields);
 
                ads_destroy(&ads);
                return ADS_ERR_OK(rc) ? 0 : -1;
        }
-       return net_run_function(argc, argv, func, net_ads_group_usage);
+       return net_run_function(c, argc, argv, "net ads group", func);
 }
 
-static int net_ads_status(int argc, const char **argv)
+static int net_ads_status(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        LDAPMessage *res;
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads status\n"
+                        "    Display machine account details\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
 
@@ -808,19 +863,21 @@ static int net_ads_status(int argc, const char **argv)
  with full control to the computer object's ACL.
 *******************************************************************/
 
-static int net_ads_leave(int argc, const char **argv)
+static int net_ads_leave(struct net_context *c, int argc, const char **argv)
 {
-       ADS_STRUCT *ads = NULL;
-       ADS_STATUS adsret;
-       NTSTATUS status;
-       int ret = -1;
-       struct cli_state *cli = NULL;
        TALLOC_CTX *ctx;
-       DOM_SID *dom_sid = NULL;
-       char *short_domain_name = NULL;
+       struct libnet_UnjoinCtx *r = NULL;
+       WERROR werr;
 
-       if (!secrets_init()) {
-               DEBUG(1,("Failed to initialise secrets database\n"));
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads leave\n"
+                        "    Leave an AD domain\n");
+               return 0;
+       }
+
+       if (!*lp_realm()) {
+               d_fprintf(stderr, "No realm set, are we joined ?\n");
                return -1;
        }
 
@@ -829,60 +886,63 @@ static int net_ads_leave(int argc, const char **argv)
                return -1;
        }
 
-       /* The finds a DC and takes care of getting the
-          user creds if necessary */
+       if (!c->opt_kerberos) {
+               use_in_memory_ccache();
+       }
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       werr = libnet_init_UnjoinCtx(ctx, &r);
+       if (!W_ERROR_IS_OK(werr)) {
+               d_fprintf(stderr, "Could not initialise unjoin context.\n");
                return -1;
        }
 
-       /* make RPC calls here */
-
-       if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap.ss,
-               ads->config.ldap_server_name)) )
-       {
+       r->in.debug             = true;
+       r->in.use_kerberos      = c->opt_kerberos;
+       r->in.dc_name           = c->opt_host;
+       r->in.domain_name       = lp_realm();
+       r->in.admin_account     = c->opt_user_name;
+       r->in.admin_password    = net_prompt_pass(c, c->opt_user_name);
+       r->in.modify_config     = lp_config_backend_is_registry();
+       r->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+                                 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+
+       werr = libnet_Unjoin(ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               d_printf("Failed to leave domain: %s\n",
+                        r->out.error_string ? r->out.error_string :
+                        get_friendly_werror_msg(werr));
                goto done;
        }
 
-       if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
+       if (W_ERROR_IS_OK(werr)) {
+               d_printf("Deleted account for '%s' in realm '%s'\n",
+                       r->in.machine_name, r->out.dns_domain_name);
                goto done;
        }
 
-       saf_delete( short_domain_name );
-
-       status = netdom_leave_domain(ctx, cli, dom_sid);
-
-       /* Try and delete it via LDAP - the old way we used to. */
-
-       adsret = ads_leave_realm(ads, global_myname());
-       if (ADS_ERR_OK(adsret)) {
-               d_printf("Deleted account for '%s' in realm '%s'\n",
-                       global_myname(), ads->config.realm);
-               ret = 0;
-       } else {
-               /* We couldn't delete it - see if the disable succeeded. */
-               if (NT_STATUS_IS_OK(status)) {
-                       d_printf("Disabled account for '%s' in realm '%s'\n",
-                               global_myname(), ads->config.realm);
-                       ret = 0;
-               } else {
-                       d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
-                               global_myname(), ads->config.realm);
-               }
+       /* We couldn't delete it - see if the disable succeeded. */
+       if (r->out.disabled_machine_account) {
+               d_printf("Disabled account for '%s' in realm '%s'\n",
+                       r->in.machine_name, r->out.dns_domain_name);
+               werr = WERR_OK;
+               goto done;
        }
 
-done:
+       d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
+                 r->in.machine_name, r->out.dns_domain_name);
 
-       if ( cli )
-               cli_shutdown(cli);
+ done:
+       TALLOC_FREE(r);
+       TALLOC_FREE(ctx);
 
-       ads_destroy(&ads);
-       TALLOC_FREE( ctx );
+       if (W_ERROR_IS_OK(werr)) {
+               return 0;
+       }
 
-       return ret;
+       return -1;
 }
 
-static NTSTATUS net_ads_join_ok(void)
+static NTSTATUS net_ads_join_ok(struct net_context *c)
 {
        ADS_STRUCT *ads = NULL;
        ADS_STATUS status;
@@ -892,9 +952,9 @@ static NTSTATUS net_ads_join_ok(void)
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       net_use_krb_machine_account();
+       net_use_krb_machine_account(c);
 
-       status = ads_startup(True, &ads);
+       status = ads_startup(c, true, &ads);
        if (!ADS_ERR_OK(status)) {
                return ads_ntstatus(status);
        }
@@ -906,13 +966,20 @@ static NTSTATUS net_ads_join_ok(void)
 /*
   check that an existing join is OK
  */
-int net_ads_testjoin(int argc, const char **argv)
+int net_ads_testjoin(struct net_context *c, int argc, const char **argv)
 {
        NTSTATUS status;
        use_in_memory_ccache();
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads testjoin\n"
+                        "    Test if the existing join is ok\n");
+               return 0;
+       }
+
        /* Display success or failure */
-       status = net_ads_join_ok();
+       status = net_ads_join_ok(c);
        if (!NT_STATUS_IS_OK(status)) {
                fprintf(stderr,"Join to domain is not valid: %s\n",
                        get_friendly_nt_error_msg(status));
@@ -927,378 +994,27 @@ int net_ads_testjoin(int argc, const char **argv)
   Simple configu checks before beginning the join
  ********************************************************************/
 
-static NTSTATUS check_ads_config( void )
+static WERROR check_ads_config( void )
 {
        if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
                d_printf("Host is not configured as a member server.\n");
-               return NT_STATUS_INVALID_DOMAIN_ROLE;
+               return WERR_INVALID_DOMAIN_ROLE;
        }
 
        if (strlen(global_myname()) > 15) {
                d_printf("Our netbios name can be at most 15 chars long, "
                         "\"%s\" is %u chars long\n", global_myname(),
                         (unsigned int)strlen(global_myname()));
-               return NT_STATUS_NAME_TOO_LONG;
+               return WERR_INVALID_COMPUTERNAME;
        }
 
        if ( lp_security() == SEC_ADS && !*lp_realm()) {
                d_fprintf(stderr, "realm must be set in in %s for ADS "
                        "join to succeed.\n", get_dyn_CONFIGFILE());
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       if (!secrets_init()) {
-               DEBUG(1,("Failed to initialise secrets database\n"));
-               /* This is a good bet for failure of secrets_init ... */
-               return NT_STATUS_ACCESS_DENIED;
-       }
-
-       return NT_STATUS_OK;
-}
-
-/*******************************************************************
- Do the domain join
- ********************************************************************/
-
-static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
-                               struct sockaddr_storage *pss, char **domain,
-                               DOM_SID **dom_sid,
-                               const char *password)
-{
-       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
-       struct cli_state *cli = NULL;
-
-       ret = connect_to_ipc_krb5(&cli, pss, servername);
-       if ( !NT_STATUS_IS_OK(ret) ) {
-               goto done;
-       }
-
-       ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
-       if ( !NT_STATUS_IS_OK(ret) ) {
-               goto done;
-       }
-
-       /* cli->server_domain is not filled in when using krb5
-          session setups */
-
-       saf_store( *domain, cli->desthost );
-
-       ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
-
-done:
-       if ( cli )
-               cli_shutdown(cli);
-
-       return ret;
-}
-
-/*******************************************************************
- Set a machines dNSHostName and servicePrincipalName attributes
- ********************************************************************/
-
-static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
-{
-       ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
-       char *new_dn;
-       ADS_MODLIST mods;
-       const char *servicePrincipalName[3] = {NULL, NULL, NULL};
-       char *psp;
-       fstring my_fqdn;
-       LDAPMessage *res = NULL;
-       char *dn_string = NULL;
-       const char *machine_name = global_myname();
-       int count;
-
-       if ( !machine_name ) {
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       /* Find our DN */
-
-       status = ads_find_machine_acct(ads_s, &res, machine_name);
-       if (!ADS_ERR_OK(status))
-               return status;
-
-       if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
-               DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
-               DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
-               goto done;
-       }
-
-       new_dn = talloc_strdup(ctx, dn_string);
-       ads_memfree(ads_s, dn_string);
-       if (!new_dn) {
-               return ADS_ERROR(LDAP_NO_MEMORY);
+               return WERR_INVALID_PARAM;
        }
 
-       /* Windows only creates HOST/shortname & HOST/fqdn. */
-
-       if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
-               goto done;
-       strupper_m(psp);
-       servicePrincipalName[0] = psp;
-
-       name_to_fqdn(my_fqdn, machine_name);
-       strlower_m(my_fqdn);
-       if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
-               goto done;
-       servicePrincipalName[1] = psp;
-
-       if (!(mods = ads_init_mods(ctx))) {
-               goto done;
-       }
-
-       /* fields of primary importance */
-
-       ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
-       ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
-
-       status = ads_gen_mod(ads_s, new_dn, mods);
-
-done:
-       ads_msgfree(ads_s, res);
-
-       return status;
-}
-
-/*******************************************************************
- Set a machines dNSHostName and servicePrincipalName attributes
- ********************************************************************/
-
-static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
-{
-       ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
-       char *new_dn;
-       ADS_MODLIST mods;
-       LDAPMessage *res = NULL;
-       char *dn_string = NULL;
-       const char *machine_name = global_myname();
-       int count;
-
-       if ( !machine_name ) {
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       /* Find our DN */
-
-       status = ads_find_machine_acct(ads_s, &res, machine_name);
-       if (!ADS_ERR_OK(status))
-               return status;
-
-       if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
-               DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
-               DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
-               goto done;
-       }
-
-       new_dn = talloc_strdup(ctx, dn_string);
-       ads_memfree(ads_s, dn_string);
-       if (!new_dn) {
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       /* now do the mods */
-
-       if (!(mods = ads_init_mods(ctx))) {
-               goto done;
-       }
-
-       /* fields of primary importance */
-
-       ads_mod_str(ctx, &mods, "userPrincipalName", upn);
-
-       status = ads_gen_mod(ads_s, new_dn, mods);
-
-done:
-       ads_msgfree(ads_s, res);
-
-       return status;
-}
-
-/*******************************************************************
- Set a machines dNSHostName and servicePrincipalName attributes
- ********************************************************************/
-
-static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
-                                       const char *os_name, const char *os_version )
-{
-       ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
-       char *new_dn;
-       ADS_MODLIST mods;
-       LDAPMessage *res = NULL;
-       char *dn_string = NULL;
-       const char *machine_name = global_myname();
-       int count;
-       char *os_sp = NULL;
-
-       if ( !os_name || !os_version ) {
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       /* Find our DN */
-
-       status = ads_find_machine_acct(ads_s, &res, machine_name);
-       if (!ADS_ERR_OK(status))
-               return status;
-
-       if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
-               DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
-               DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
-               goto done;
-       }
-
-       new_dn = talloc_strdup(ctx, dn_string);
-       ads_memfree(ads_s, dn_string);
-       if (!new_dn) {
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       /* now do the mods */
-
-       if (!(mods = ads_init_mods(ctx))) {
-               goto done;
-       }
-
-       os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
-
-       /* fields of primary importance */
-
-       ads_mod_str(ctx, &mods, "operatingSystem", os_name);
-       ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
-       if ( os_sp )
-               ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
-
-       status = ads_gen_mod(ads_s, new_dn, mods);
-
-done:
-       ads_msgfree(ads_s, res);
-       TALLOC_FREE( os_sp );
-
-       return status;
-}
-
-/*******************************************************************
-  join a domain using ADS (LDAP mods)
- ********************************************************************/
-
-static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
-{
-       ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
-       char *ou_str = NULL;
-       char *dn = NULL;
-       LDAPMessage *res = NULL;
-       bool moved;
-
-       ou_str = ads_ou_string(ads, ou);
-       if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
-               rc = ADS_ERROR(LDAP_NO_MEMORY);
-               goto done;
-       }
-
-       rc = ads_search_dn(ads, &res, dn, NULL);
-       if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "The specified OU does not exist.\n");
-               goto done;
-       }
-
-               /* Attempt to create the machine account and bail if this fails.
-                  Assume that the admin wants exactly what they requested */
-
-               rc = ads_create_machine_acct( ads, global_myname(), dn );
-       if (ADS_ERR_OK(rc)) {
-               DEBUG(1, ("machine account created\n"));
-               goto done;
-               }
-       if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
-               DEBUG(1, ("machine account creation failed\n"));
-               goto done;
-       }
-
-       rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
-       if (!ADS_ERR_OK(rc)) {
-               DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
-               goto done;
-       }
-
-       if (moved) {
-               d_printf("The machine account was moved into the specified OU.\n");
-       } else {
-               d_printf("The machine account already exists in the specified OU.\n");
-       }
-
-done:
-       ads_msgfree(ads, res);
-       SAFE_FREE( ou_str );
-       SAFE_FREE( dn );
-
-       return rc;
-}
-
-/************************************************************************
- ************************************************************************/
-
-static bool net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
-{
-       uint32 domain_func;
-       ADS_STATUS status;
-       fstring salt;
-       char *std_salt;
-       LDAPMessage *res = NULL;
-       const char *machine_name = global_myname();
-
-       status = ads_domain_func_level( ads, &domain_func );
-       if ( !ADS_ERR_OK(status) ) {
-               DEBUG(2,("Failed to determine domain functional level!\n"));
-               return False;
-       }
-
-       /* go ahead and setup the default salt */
-
-       if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
-               d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
-               return False;
-       }
-
-       fstrcpy( salt, std_salt );
-       SAFE_FREE( std_salt );
-
-       /* if it's a Windows functional domain, we have to look for the UPN */
-
-       if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
-               char *upn;
-               int count;
-
-               status = ads_find_machine_acct(ads, &res, machine_name);
-               if (!ADS_ERR_OK(status)) {
-                       return False;
-               }
-
-               if ( (count = ads_count_replies(ads, res)) != 1 ) {
-                       DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
-                       return False;
-               }
-
-               upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
-               if ( upn ) {
-                       fstrcpy( salt, upn );
-               }
-
-               ads_msgfree(ads, res);
-       }
-
-       return kerberos_secrets_store_des_salt( salt );
+       return WERR_OK;
 }
 
 /*******************************************************************
@@ -1308,14 +1024,13 @@ static bool net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
 #if defined(WITH_DNS_UPDATES)
 #include "dns.h"
 DNS_ERROR DoDNSUpdate(char *pszServerName,
-                     const char *pszDomainName,
-                     const char *pszHostName,
-                     const struct in_addr *iplist, int num_addrs );
-
+                     const char *pszDomainName, const char *pszHostName,
+                     const struct sockaddr_storage *sslist,
+                     size_t num_addrs );
 
 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
                                        const char *machine_name,
-                                       const struct in_addr *addrs,
+                                       const struct sockaddr_storage *addrs,
                                        int num_addrs)
 {
        struct dns_rr_ns *nameservers = NULL;
@@ -1404,7 +1119,7 @@ done:
 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
 {
        int num_addrs;
-       struct in_addr *iplist = NULL;
+       struct sockaddr_storage *iplist = NULL;
        fstring machine_name;
        NTSTATUS status;
 
@@ -1432,7 +1147,7 @@ static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
 /*******************************************************************
  ********************************************************************/
 
-static int net_ads_join_usage(int argc, const char **argv)
+static int net_ads_join_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf("net ads join [options]\n");
        d_printf("Valid options:\n");
@@ -1455,53 +1170,44 @@ static int net_ads_join_usage(int argc, const char **argv)
 /*******************************************************************
  ********************************************************************/
 
-int net_ads_join(int argc, const char **argv)
+int net_ads_join(struct net_context *c, int argc, const char **argv)
 {
-       ADS_STRUCT *ads = NULL;
-       ADS_STATUS status;
-       NTSTATUS nt_status;
-       char *machine_account = NULL;
-       char *short_domain_name = NULL;
-       char *tmp_password, *password;
        TALLOC_CTX *ctx = NULL;
-       DOM_SID *domain_sid = NULL;
-       bool createupn = False;
+       struct libnet_JoinCtx *r = NULL;
+       const char *domain = lp_realm();
+       WERROR werr = WERR_SETUP_NOT_JOINED;
+       bool createupn = false;
        const char *machineupn = NULL;
        const char *create_in_ou = NULL;
        int i;
-       fstring dc_name;
-       struct sockaddr_storage dcss;
        const char *os_name = NULL;
        const char *os_version = NULL;
+       bool modify_config = lp_config_backend_is_registry();
 
-       nt_status = check_ads_config();
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
-               goto fail;
-       }
+       if (c->display_usage)
+               return net_ads_join_usage(c, argc, argv);
 
-       /* find a DC to initialize the server affinity cache */
+       if (!modify_config) {
 
-       get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcss );
+               werr = check_ads_config();
+               if (!W_ERROR_IS_OK(werr)) {
+                       d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
+                       goto fail;
+               }
+       }
 
-       status = ads_startup(True, &ads);
-       if (!ADS_ERR_OK(status)) {
-               DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
-               nt_status = ads_ntstatus(status);
+       if (!(ctx = talloc_init("net_ads_join"))) {
+               d_fprintf(stderr, "Could not initialise talloc context.\n");
+               werr = WERR_NOMEM;
                goto fail;
        }
 
-       if (strcmp(ads->config.realm, lp_realm()) != 0) {
-               d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
-                       "(%s) DO NOT match.  Aborting join\n",
-                       ads->config.realm, get_dyn_CONFIGFILE(), lp_realm());
-               nt_status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
+       if (!c->opt_kerberos) {
+               use_in_memory_ccache();
        }
 
-       if (!(ctx = talloc_init("net_ads_join"))) {
-               d_fprintf(stderr, "Could not initialise talloc context.\n");
-               nt_status = NT_STATUS_NO_MEMORY;
+       werr = libnet_init_JoinCtx(ctx, &r);
+       if (!W_ERROR_IS_OK(werr)) {
                goto fail;
        }
 
@@ -1509,234 +1215,130 @@ int net_ads_join(int argc, const char **argv)
 
        for ( i=0; i<argc; i++ ) {
                if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
-                       createupn = True;
+                       createupn = true;
                        machineupn = get_string_param(argv[i]);
                }
                else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
                        if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
                                d_fprintf(stderr, "Please supply a valid OU path.\n");
-                               nt_status = NT_STATUS_INVALID_PARAMETER;
+                               werr = WERR_INVALID_PARAM;
                                goto fail;
                        }
                }
                else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
                        if ( (os_name = get_string_param(argv[i])) == NULL ) {
                                d_fprintf(stderr, "Please supply a operating system name.\n");
-                               nt_status = NT_STATUS_INVALID_PARAMETER;
+                               werr = WERR_INVALID_PARAM;
                                goto fail;
                        }
                }
                else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
                        if ( (os_version = get_string_param(argv[i])) == NULL ) {
                                d_fprintf(stderr, "Please supply a valid operating system version.\n");
-                               nt_status = NT_STATUS_INVALID_PARAMETER;
+                               werr = WERR_INVALID_PARAM;
                                goto fail;
                        }
                }
                else {
-                       d_fprintf(stderr, "Bad option: %s\n", argv[i]);
-                       nt_status = NT_STATUS_INVALID_PARAMETER;
-                       goto fail;
+                       domain = argv[i];
                }
        }
 
-       /* If we were given an OU, try to create the machine in
-          the OU account first and then do the normal RPC join */
-
-       if  ( create_in_ou ) {
-               status = net_precreate_machine_acct( ads, create_in_ou );
-               if ( !ADS_ERR_OK(status) ) {
-                       d_fprintf( stderr, "Failed to pre-create the machine object "
-                               "in OU %s.\n", create_in_ou);
-                       DEBUG(1, ("error calling net_precreate_machine_acct: %s\n", 
-                                 ads_errstr(status)));
-                       nt_status = ads_ntstatus(status);
-                       goto fail;
-               }
+       if (!*domain) {
+               d_fprintf(stderr, "Please supply a valid domain name\n");
+               werr = WERR_INVALID_PARAM;
+               goto fail;
        }
 
        /* Do the domain join here */
 
-       tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-       password = talloc_strdup(ctx, tmp_password);
-
-       nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
-                                   &ads->ldap.ss, &short_domain_name, &domain_sid, password);
-       if ( !NT_STATUS_IS_OK(nt_status) ) {
-               DEBUG(1, ("call of net_join_domain failed: %s\n",
-                         get_friendly_nt_error_msg(nt_status)));
+       r->in.domain_name       = domain;
+       r->in.create_upn        = createupn;
+       r->in.upn               = machineupn;
+       r->in.account_ou        = create_in_ou;
+       r->in.os_name           = os_name;
+       r->in.os_version        = os_version;
+       r->in.dc_name           = c->opt_host;
+       r->in.admin_account     = c->opt_user_name;
+       r->in.admin_password    = net_prompt_pass(c, c->opt_user_name);
+       r->in.debug             = true;
+       r->in.use_kerberos      = c->opt_kerberos;
+       r->in.modify_config     = modify_config;
+       r->in.join_flags        = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+                                 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
+                                 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
+
+       werr = libnet_Join(ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
                goto fail;
        }
 
        /* Check the short name of the domain */
 
-       if ( !strequal(lp_workgroup(), short_domain_name) ) {
+       if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
                d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
                d_printf("domain name obtained from the server.\n");
-               d_printf("Using the name [%s] from the server.\n", short_domain_name);
+               d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name);
                d_printf("You should set \"workgroup = %s\" in %s.\n",
-                        short_domain_name, get_dyn_CONFIGFILE());
-       }
-
-       d_printf("Using short domain name -- %s\n", short_domain_name);
-
-       /*  HACK ALERT!  Store the sid and password under both the lp_workgroup() 
-           value from smb.conf and the string returned from the server.  The former is
-           neede to bootstrap winbindd's first connection to the DC to get the real 
-           short domain name   --jerry */
-
-       if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
-               || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
-       {
-               /* issue an internal error here for now.
-                * everything else would mean changing tdb routines. */
-               nt_status = NT_STATUS_INTERNAL_ERROR;
-               goto fail;
+                        r->out.netbios_domain_name, get_dyn_CONFIGFILE());
        }
 
-       /* Verify that everything is ok */
-
-       nt_status = net_rpc_join_ok(short_domain_name,
-                                   ads->config.ldap_server_name, &ads->ldap.ss);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               d_fprintf(stderr,
-                         "Failed to verify membership in domain: %s!\n",
-                         nt_errstr(nt_status));
-               goto fail;
-       }
-
-       /* create the dNSHostName & servicePrincipalName values */
-
-       status = net_set_machine_spn( ctx, ads );
-       if ( !ADS_ERR_OK(status) )  {
-
-               d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
-               d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
-               d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
-
-               /* Disable the machine account in AD.  Better to fail than to leave 
-                  a confused admin.  */
-
-               if ( net_ads_leave( 0, NULL ) != 0 ) {
-                       d_fprintf( stderr, "Failed to disable machine account in AD.  Please do so manually.\n");
-               }
-
-               /* clear out the machine password */
-
-               netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
-               netdom_store_machine_account( short_domain_name, domain_sid, "" );
+       d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name);
 
-               nt_status = ads_ntstatus(status);
-               goto fail;
+       if (r->out.dns_domain_name) {
+               d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name,
+                       r->out.dns_domain_name);
+       } else {
+               d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name,
+                       r->out.netbios_domain_name);
        }
 
-       if ( !net_derive_salting_principal( ctx, ads ) ) {
-               DEBUG(1,("Failed to determine salting principal\n"));
-               goto fail;
-       }
+#if defined(WITH_DNS_UPDATES)
+       if (r->out.domain_is_ad) {
+               /* We enter this block with user creds */
+               ADS_STRUCT *ads_dns = NULL;
 
-       if ( createupn ) {
-               char *upn;
+               if ( (ads_dns = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
+                       /* kinit with the machine password */
 
-               /* default to using the short UPN name */
-               if (!machineupn ) {
-                       upn = talloc_asprintf(ctx,
-                                       "host/%s@%s", global_myname(),
-                                       ads->config.realm );
-                       if (!upn) {
-                               nt_status = NT_STATUS_NO_MEMORY;
+                       use_in_memory_ccache();
+                       if (asprintf( &ads_dns->auth.user_name, "%s$", global_myname()) == -1) {
                                goto fail;
                        }
-                       machineupn = upn;
+                       ads_dns->auth.password = secrets_fetch_machine_password(
+                               r->out.netbios_domain_name, NULL, NULL );
+                       ads_dns->auth.realm = SMB_STRDUP( r->out.dns_domain_name );
+                       strupper_m(ads_dns->auth.realm );
+                       ads_kinit_password( ads_dns );
                }
 
-               status = net_set_machine_upn( ctx, ads, machineupn );
-               if ( !ADS_ERR_OK(status) )  {
-                       d_fprintf(stderr, "Failed to set userPrincipalName.  Are you a Domain Admin?\n");
+               if ( !ads_dns || !NT_STATUS_IS_OK(net_update_dns( ctx, ads_dns )) ) {
+                       d_fprintf( stderr, "DNS update failed!\n" );
                }
-       }
-
-       /* Try to set the operatingSystem attributes if asked */
 
-       if ( os_name && os_version ) {
-               status = net_set_os_attributes( ctx, ads, os_name, os_version );
-               if ( !ADS_ERR_OK(status) )  {
-                       d_fprintf(stderr, "Failed to set operatingSystem attributes.  "
-                                 "Are you a Domain Admin?\n");
-               }
-       }
-
-       /* Now build the keytab, using the same ADS connection */
-
-       if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
-               DEBUG(1,("Error creating host keytab!\n"));
+               /* exit from this block using machine creds */
+               ads_destroy(&ads_dns);
        }
-
-#if defined(WITH_DNS_UPDATES)
-       /* We enter this block with user creds */
-       ads_kdestroy( NULL );
-       ads_destroy(&ads);
-       ads = NULL;
-
-       if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
-               /* kinit with the machine password */
-
-               use_in_memory_ccache();
-               asprintf( &ads->auth.user_name, "%s$", global_myname() );
-               ads->auth.password = secrets_fetch_machine_password(
-                       lp_workgroup(), NULL, NULL );
-               ads->auth.realm = SMB_STRDUP( lp_realm() );
-               ads_kinit_password( ads );
-       }
-
-       if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
-               d_fprintf( stderr, "DNS update failed!\n" );
-       }
-
-       /* exit from this block using machine creds */
 #endif
-
-       d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
-
-       SAFE_FREE(machine_account);
+       TALLOC_FREE(r);
        TALLOC_FREE( ctx );
-       ads_destroy(&ads);
 
        return 0;
 
 fail:
        /* issue an overall failure message at the end. */
-       d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
-
-       SAFE_FREE(machine_account);
+       d_printf("Failed to join domain: %s\n",
+               r && r->out.error_string ? r->out.error_string :
+               get_friendly_werror_msg(werr));
        TALLOC_FREE( ctx );
-        ads_destroy(&ads);
 
         return -1;
-
 }
 
 /*******************************************************************
  ********************************************************************/
 
-static int net_ads_dns_usage(int argc, const char **argv)
-{
-#if defined(WITH_DNS_UPDATES)
-       d_printf("net ads dns <command>\n");
-       d_printf("Valid commands:\n");
-       d_printf("   register         Issue a dynamic DNS update request for our hostname\n");
-
-       return 0;
-#else
-       d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
-       return -1;
-#endif
-}
-
-/*******************************************************************
- ********************************************************************/
-
-static int net_ads_dns_register(int argc, const char **argv)
+static int net_ads_dns_register(struct net_context *c, int argc, const char **argv)
 {
 #if defined(WITH_DNS_UPDATES)
        ADS_STRUCT *ads;
@@ -1747,8 +1349,10 @@ static int net_ads_dns_register(int argc, const char **argv)
        talloc_enable_leak_report();
 #endif
 
-       if (argc > 0) {
-               d_fprintf(stderr, "net ads dns register\n");
+       if (argc > 0 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads dns register\n"
+                        "    Register hostname with DNS\n");
                return -1;
        }
 
@@ -1757,7 +1361,7 @@ static int net_ads_dns_register(int argc, const char **argv)
                return -1;
        }
 
-       status = ads_startup(True, &ads);
+       status = ads_startup(c, true, &ads);
        if ( !ADS_ERR_OK(status) ) {
                DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
                TALLOC_FREE(ctx);
@@ -1787,7 +1391,7 @@ static int net_ads_dns_register(int argc, const char **argv)
 DNS_ERROR do_gethostbyname(const char *server, const char *host);
 #endif
 
-static int net_ads_dns_gethostbyname(int argc, const char **argv)
+static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char **argv)
 {
 #if defined(WITH_DNS_UPDATES)
        DNS_ERROR err;
@@ -1796,9 +1400,12 @@ static int net_ads_dns_gethostbyname(int argc, const char **argv)
        talloc_enable_leak_report();
 #endif
 
-       if (argc != 2) {
-               d_fprintf(stderr, "net ads dns gethostbyname <server> "
-                         "<name>\n");
+       if (argc != 2 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads dns gethostbyname <server> <name>\n"
+                        "  Look up hostname from the AD\n"
+                        "    server\tName server to use\n"
+                        "    name\tName to look up\n");
                return -1;
        }
 
@@ -1809,21 +1416,35 @@ static int net_ads_dns_gethostbyname(int argc, const char **argv)
        return 0;
 }
 
-static int net_ads_dns(int argc, const char *argv[])
+static int net_ads_dns(struct net_context *c, int argc, const char *argv[])
 {
        struct functable func[] = {
-               {"REGISTER", net_ads_dns_register},
-               {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
-               {NULL, NULL}
+               {
+                       "register",
+                       net_ads_dns_register,
+                       NET_TRANSPORT_ADS,
+                       "Add host dns entry to AD",
+                       "net ads dns register\n"
+                       "    Add host dns entry to AD"
+               },
+               {
+                       "gethostbyname",
+                       net_ads_dns_gethostbyname,
+                       NET_TRANSPORT_ADS,
+                       "Look up host",
+                       "net ads dns gethostbyname\n"
+                       "    Look up host"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(argc, argv, func, net_ads_dns_usage);
+       return net_run_function(c, argc, argv, "net ads dns", func);
 }
 
 /*******************************************************************
  ********************************************************************/
 
-int net_ads_printer_usage(int argc, const char **argv)
+int net_ads_printer_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf(
 "\nnet ads printer search <printer>"
@@ -1843,13 +1464,20 @@ int net_ads_printer_usage(int argc, const char **argv)
 /*******************************************************************
  ********************************************************************/
 
-static int net_ads_printer_search(int argc, const char **argv)
+static int net_ads_printer_search(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        LDAPMessage *res = NULL;
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer search\n"
+                        "    List printers in the AD\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -1875,14 +1503,23 @@ static int net_ads_printer_search(int argc, const char **argv)
        return 0;
 }
 
-static int net_ads_printer_info(int argc, const char **argv)
+static int net_ads_printer_info(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        const char *servername, *printername;
        LDAPMessage *res = NULL;
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer info [printername [servername]]\n"
+                        "  Display printer info from AD\n"
+                        "    printername\tPrinter name or wildcard\n"
+                        "    servername\tName of the print server\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -1922,7 +1559,7 @@ static int net_ads_printer_info(int argc, const char **argv)
        return 0;
 }
 
-static int net_ads_printer_publish(int argc, const char **argv)
+static int net_ads_printer_publish(struct net_context *c, int argc, const char **argv)
 {
         ADS_STRUCT *ads;
         ADS_STATUS rc;
@@ -1937,14 +1574,19 @@ static int net_ads_printer_publish(int argc, const char **argv)
        char *srv_cn_escaped = NULL, *printername_escaped = NULL;
        LDAPMessage *res = NULL;
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (argc < 1 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer publish <printername> [servername]\n"
+                        "  Publish printer in AD\n"
+                        "    printername\tName of the printer\n"
+                        "    servername\tName of the print server\n");
                talloc_destroy(mem_ctx);
                return -1;
        }
 
-       if (argc < 1) {
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                talloc_destroy(mem_ctx);
-               return net_ads_printer_usage(argc, argv);
+               return -1;
        }
 
        printername = argv[0];
@@ -1962,8 +1604,8 @@ static int net_ads_printer_publish(int argc, const char **argv)
        nt_status = cli_full_connection(&cli, global_myname(), servername,
                                        &server_ss, 0,
                                        "IPC$", "IPC",
-                                       opt_user_name, opt_workgroup,
-                                       opt_password ? opt_password : "",
+                                       c->opt_user_name, c->opt_workgroup,
+                                       c->opt_password ? c->opt_password : "",
                                        CLI_FULL_CONNECTION_USE_KERBEROS,
                                        Undefined, NULL);
 
@@ -2001,13 +1643,20 @@ static int net_ads_printer_publish(int argc, const char **argv)
                return -1;
        }
 
-       asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
+       if (asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn) == -1) {
+               SAFE_FREE(srv_cn_escaped);
+               SAFE_FREE(printername_escaped);
+               d_fprintf(stderr, "Internal error, out of memory!");
+               ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
+               return -1;
+       }
 
        SAFE_FREE(srv_cn_escaped);
        SAFE_FREE(printername_escaped);
 
-       pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
-       if (!pipe_hnd) {
+       nt_status = cli_rpc_pipe_open_noauth(cli, &syntax_spoolss, &pipe_hnd);
+       if (!NT_STATUS_IS_OK(nt_status)) {
                d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
                         servername);
                SAFE_FREE(prt_dn);
@@ -2041,7 +1690,7 @@ static int net_ads_printer_publish(int argc, const char **argv)
        return 0;
 }
 
-static int net_ads_printer_remove(int argc, const char **argv)
+static int net_ads_printer_remove(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -2049,12 +1698,17 @@ static int net_ads_printer_remove(int argc, const char **argv)
        char *prt_dn;
        LDAPMessage *res = NULL;
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (argc < 1 || c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads printer remove <printername> [servername]\n"
+                        "  Remove a printer from the AD\n"
+                        "    printername\tName of the printer\n"
+                        "    servername\tName of the print server\n");
                return -1;
        }
 
-       if (argc < 1) {
-               return net_ads_printer_usage(argc, argv);
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+               return -1;
        }
 
        if (argc > 1) {
@@ -2094,32 +1748,68 @@ static int net_ads_printer_remove(int argc, const char **argv)
        return 0;
 }
 
-static int net_ads_printer(int argc, const char **argv)
+static int net_ads_printer(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"SEARCH", net_ads_printer_search},
-               {"INFO", net_ads_printer_info},
-               {"PUBLISH", net_ads_printer_publish},
-               {"REMOVE", net_ads_printer_remove},
-               {NULL, NULL}
+               {
+                       "search",
+                       net_ads_printer_search,
+                       NET_TRANSPORT_ADS,
+                       "Search for a printer",
+                       "net ads printer search\n"
+                       "    Search for a printer"
+               },
+               {
+                       "info",
+                       net_ads_printer_info,
+                       NET_TRANSPORT_ADS,
+                       "Display printer information",
+                       "net ads printer info\n"
+                       "    Display printer information"
+               },
+               {
+                       "publish",
+                       net_ads_printer_publish,
+                       NET_TRANSPORT_ADS,
+                       "Publish a printer",
+                       "net ads printer publish\n"
+                       "    Publish a printer"
+               },
+               {
+                       "remove",
+                       net_ads_printer_remove,
+                       NET_TRANSPORT_ADS,
+                       "Delete a printer",
+                       "net ads printer remove\n"
+                       "    Delete a printer"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(argc, argv, func, net_ads_printer_usage);
+       return net_run_function(c, argc, argv, "net ads printer", func);
 }
 
 
-static int net_ads_password(int argc, const char **argv)
+static int net_ads_password(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
-       const char *auth_principal = opt_user_name;
-       const char *auth_password = opt_password;
+       const char *auth_principal = c->opt_user_name;
+       const char *auth_password = c->opt_password;
        char *realm = NULL;
        char *new_password = NULL;
-       char *c, *prompt;
+       char *chr, *prompt;
        const char *user;
        ADS_STATUS ret;
 
-       if (opt_user_name == NULL || opt_password == NULL) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads password <username>\n"
+                        "  Change password for user\n"
+                        "    username\tName of user to change password for\n");
+               return 0;
+       }
+
+       if (c->opt_user_name == NULL || c->opt_password == NULL) {
                d_fprintf(stderr, "You must supply an administrator username/password\n");
                return -1;
        }
@@ -2131,21 +1821,23 @@ static int net_ads_password(int argc, const char **argv)
 
        user = argv[0];
        if (!strchr_m(user, '@')) {
-               asprintf(&c, "%s@%s", argv[0], lp_realm());
-               user = c;
+               if (asprintf(&chr, "%s@%s", argv[0], lp_realm()) == -1) {
+                       return -1;
+               }
+               user = chr;
        }
 
        use_in_memory_ccache();
-       c = strchr_m(auth_principal, '@');
-       if (c) {
-               realm = ++c;
+       chr = strchr_m(auth_principal, '@');
+       if (chr) {
+               realm = ++chr;
        } else {
                realm = lp_realm();
        }
 
        /* use the realm so we can eventually change passwords for users
        in realms other than default */
-       if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
+       if (!(ads = ads_init(realm, c->opt_workgroup, c->opt_host))) {
                return -1;
        }
 
@@ -2153,7 +1845,7 @@ static int net_ads_password(int argc, const char **argv)
                fill in the KDC's addresss */
        ads_connect(ads);
 
-       if (!ads || !ads->config.realm) {
+       if (!ads->config.realm) {
                d_fprintf(stderr, "Didn't find the kerberos server!\n");
                return -1;
        }
@@ -2161,7 +1853,9 @@ static int net_ads_password(int argc, const char **argv)
        if (argv[1]) {
                new_password = (char *)argv[1];
        } else {
-               asprintf(&prompt, "Enter new password for %s:", user);
+               if (asprintf(&prompt, "Enter new password for %s:", user) == -1) {
+                       return -1;
+               }
                new_password = getpass(prompt);
                free(prompt);
        }
@@ -2180,29 +1874,39 @@ static int net_ads_password(int argc, const char **argv)
        return 0;
 }
 
-int net_ads_changetrustpw(int argc, const char **argv)
+int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        char *host_principal;
        fstring my_name;
        ADS_STATUS ret;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads changetrustpw\n"
+                        "    Change the machine account's trust password\n");
+               return 0;
+       }
+
        if (!secrets_init()) {
                DEBUG(1,("Failed to initialise secrets database\n"));
                return -1;
        }
 
-       net_use_krb_machine_account();
+       net_use_krb_machine_account(c);
 
        use_in_memory_ccache();
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
 
        fstrcpy(my_name, global_myname());
        strlower_m(my_name);
-       asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
+       if (asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm) == -1) {
+               ads_destroy(&ads);
+               return -1;
+       }
        d_printf("Changing password for principal: %s\n", host_principal);
 
        ret = ads_change_trust_account_password(ads, host_principal);
@@ -2232,16 +1936,16 @@ int net_ads_changetrustpw(int argc, const char **argv)
 /*
   help for net ads search
 */
-static int net_ads_search_usage(int argc, const char **argv)
+static int net_ads_search_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf(
-               "\nnet ads search <expression> <attributes...>\n"\
-               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
-               "The expression is a standard LDAP search expression, and the\n"\
-               "attributes are a list of LDAP fields to show in the results\n\n"\
+               "\nnet ads search <expression> <attributes...>\n"
+               "\nPerform a raw LDAP search on a ADS server and dump the results.\n"
+               "The expression is a standard LDAP search expression, and the\n"
+               "attributes are a list of LDAP fields to show in the results.\n\n"
                "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
                );
-       net_common_flags_usage(argc, argv);
+       net_common_flags_usage(c, argc, argv);
        return -1;
 }
 
@@ -2249,7 +1953,7 @@ static int net_ads_search_usage(int argc, const char **argv)
 /*
   general ADS search function. Useful in diagnosing problems in ADS
 */
-static int net_ads_search(int argc, const char **argv)
+static int net_ads_search(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -2257,11 +1961,11 @@ static int net_ads_search(int argc, const char **argv)
        const char **attrs;
        LDAPMessage *res = NULL;
 
-       if (argc < 1) {
-               return net_ads_search_usage(argc, argv);
+       if (argc < 1 || c->display_usage) {
+               return net_ads_search_usage(c, argc, argv);
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -2292,17 +1996,17 @@ static int net_ads_search(int argc, const char **argv)
 /*
   help for net ads search
 */
-static int net_ads_dn_usage(int argc, const char **argv)
+static int net_ads_dn_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf(
-               "\nnet ads dn <dn> <attributes...>\n"\
-               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
-               "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
-               "to show in the results\n\n"\
+               "\nnet ads dn <dn> <attributes...>\n"
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"
+               "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"
+               "to show in the results\n\n"
                "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
                "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
                );
-       net_common_flags_usage(argc, argv);
+       net_common_flags_usage(c, argc, argv);
        return -1;
 }
 
@@ -2310,7 +2014,7 @@ static int net_ads_dn_usage(int argc, const char **argv)
 /*
   general ADS search function. Useful in diagnosing problems in ADS
 */
-static int net_ads_dn(int argc, const char **argv)
+static int net_ads_dn(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -2318,11 +2022,11 @@ static int net_ads_dn(int argc, const char **argv)
        const char **attrs;
        LDAPMessage *res = NULL;
 
-       if (argc < 1) {
-               return net_ads_dn_usage(argc, argv);
+       if (argc < 1 || c->display_usage) {
+               return net_ads_dn_usage(c, argc, argv);
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -2352,16 +2056,16 @@ static int net_ads_dn(int argc, const char **argv)
 /*
   help for net ads sid search
 */
-static int net_ads_sid_usage(int argc, const char **argv)
+static int net_ads_sid_usage(struct net_context *c, int argc, const char **argv)
 {
        d_printf(
-               "\nnet ads sid <sid> <attributes...>\n"\
-               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
-               "The SID is in string format, and the attributes are a list of LDAP fields \n"\
-               "to show in the results\n\n"\
+               "\nnet ads sid <sid> <attributes...>\n"
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"
+               "The SID is in string format, and the attributes are a list of LDAP fields \n"
+               "to show in the results\n\n"
                "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
                );
-       net_common_flags_usage(argc, argv);
+       net_common_flags_usage(c, argc, argv);
        return -1;
 }
 
@@ -2369,7 +2073,7 @@ static int net_ads_sid_usage(int argc, const char **argv)
 /*
   general ADS search function. Useful in diagnosing problems in ADS
 */
-static int net_ads_sid(int argc, const char **argv)
+static int net_ads_sid(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
@@ -2378,11 +2082,11 @@ static int net_ads_sid(int argc, const char **argv)
        LDAPMessage *res = NULL;
        DOM_SID sid;
 
-       if (argc < 1) {
-               return net_ads_sid_usage(argc, argv);
+       if (argc < 1 || c->display_usage) {
+               return net_ads_sid_usage(c, argc, argv);
        }
 
-       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
                return -1;
        }
 
@@ -2413,35 +2117,19 @@ static int net_ads_sid(int argc, const char **argv)
        return 0;
 }
 
-
-static int net_ads_keytab_usage(int argc, const char **argv)
-{
-       d_printf(
-               "net ads keytab <COMMAND>\n"\
-"<COMMAND> can be either:\n"\
-"  ADD       Adds new service principal\n"\
-"  CREATE    Creates a fresh keytab\n"\
-"  FLUSH     Flushes out all keytab entries\n"\
-"  HELP      Prints this help message\n"\
-"  LIST      List the keytab\n"\
-"The ADD and LIST command will take arguments, the other commands\n"\
-"will not take any arguments.   The arguments given to ADD\n"\
-"should be a list of principals to add.  For example, \n"\
-"   net ads keytab add srv1 srv2\n"\
-"will add principals for the services srv1 and srv2 to the\n"\
-"system's keytab.\n"\
-"The LIST command takes a keytabname.\n"\
-"\n"
-               );
-       return -1;
-}
-
-static int net_ads_keytab_flush(int argc, const char **argv)
+static int net_ads_keytab_flush(struct net_context *c, int argc, const char **argv)
 {
        int ret;
        ADS_STRUCT *ads;
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab flush\n"
+                        "    Delete the whole keytab\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
        ret = ads_keytab_flush(ads);
@@ -2449,14 +2137,23 @@ static int net_ads_keytab_flush(int argc, const char **argv)
        return ret;
 }
 
-static int net_ads_keytab_add(int argc, const char **argv)
+static int net_ads_keytab_add(struct net_context *c, int argc, const char **argv)
 {
        int i;
        int ret = 0;
        ADS_STRUCT *ads;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab add <principal> [principal ...]\n"
+                        "  Add principals to local keytab\n"
+                        "    principal\tKerberos principal to add to "
+                        "keytab\n");
+               return 0;
+       }
+
        d_printf("Processing principals to add...\n");
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
        for (i = 0; i < argc; i++) {
@@ -2466,12 +2163,19 @@ static int net_ads_keytab_add(int argc, const char **argv)
        return ret;
 }
 
-static int net_ads_keytab_create(int argc, const char **argv)
+static int net_ads_keytab_create(struct net_context *c, int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        int ret;
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab create\n"
+                        "    Create new default keytab\n");
+               return 0;
+       }
+
+       if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
                return -1;
        }
        ret = ads_keytab_create_default(ads);
@@ -2479,10 +2183,18 @@ static int net_ads_keytab_create(int argc, const char **argv)
        return ret;
 }
 
-static int net_ads_keytab_list(int argc, const char **argv)
+static int net_ads_keytab_list(struct net_context *c, int argc, const char **argv)
 {
        const char *keytab = NULL;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads keytab list [keytab]\n"
+                        "  List a local keytab\n"
+                        "    keytab\tKeytab to list\n");
+               return 0;
+       }
+
        if (argc >= 1) {
                keytab = argv[0];
        }
@@ -2491,15 +2203,42 @@ static int net_ads_keytab_list(int argc, const char **argv)
 }
 
 
-int net_ads_keytab(int argc, const char **argv)
+int net_ads_keytab(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"ADD", net_ads_keytab_add},
-               {"CREATE", net_ads_keytab_create},
-               {"FLUSH", net_ads_keytab_flush},
-               {"HELP", net_ads_keytab_usage},
-               {"LIST", net_ads_keytab_list},
-               {NULL, NULL}
+               {
+                       "add",
+                       net_ads_keytab_add,
+                       NET_TRANSPORT_ADS,
+                       "Add a service principal",
+                       "net ads keytab add\n"
+                       "    Add a service principal"
+               },
+               {
+                       "create",
+                       net_ads_keytab_create,
+                       NET_TRANSPORT_ADS,
+                       "Create a fresh keytab",
+                       "net ads keytab create\n"
+                       "    Create a fresh keytab"
+               },
+               {
+                       "flush",
+                       net_ads_keytab_flush,
+                       NET_TRANSPORT_ADS,
+                       "Remove all keytab entries",
+                       "net ads keytab flush\n"
+                       "    Remove all keytab entries"
+               },
+               {
+                       "list",
+                       net_ads_keytab_list,
+                       NET_TRANSPORT_ADS,
+                       "List a keytab",
+                       "net ads keytab list\n"
+                       "    List a keytab"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
        if (!lp_use_kerberos_keytab()) {
@@ -2507,26 +2246,21 @@ int net_ads_keytab(int argc, const char **argv)
 use keytab functions.\n");
        }
 
-       return net_run_function(argc, argv, func, net_ads_keytab_usage);
+       return net_run_function(c, argc, argv, "net ads keytab", func);
 }
 
-static int net_ads_kerberos_usage(int argc, const char **argv)
+static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv)
 {
-       d_printf(
-               "net ads kerberos <COMMAND>\n"\
-               "<COMMAND> can be either:\n"\
-               "  RENEW     Renew TGT from existing credential cache\n"\
-               "  PAC       Dumps the Kerberos PAC\n"\
-               "  KINIT     Retrieve Ticket Granting Ticket (TGT)\n"\
-               "\n"
-       );
+       int ret = -1;
 
-       return -1;
-}
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads kerberos renew\n"
+                        "    Renew TGT from existing credential cache\n");
+               return 0;
+       }
 
-static int net_ads_kerberos_renew(int argc, const char **argv)
-{
-       int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
+       ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
        if (ret) {
                d_printf("failed to renew kerberos ticket: %s\n",
                        error_message(ret));
@@ -2534,30 +2268,37 @@ static int net_ads_kerberos_renew(int argc, const char **argv)
        return ret;
 }
 
-static int net_ads_kerberos_pac(int argc, const char **argv)
+static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv)
 {
-       PAC_DATA *pac = NULL;
-       PAC_LOGON_INFO *info = NULL;
+       struct PAC_DATA *pac = NULL;
+       struct PAC_LOGON_INFO *info = NULL;
        TALLOC_CTX *mem_ctx = NULL;
        NTSTATUS status;
        int ret = -1;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads kerberos pac\n"
+                        "    Dump the Kerberos PAC\n");
+               return 0;
+       }
+
        mem_ctx = talloc_init("net_ads_kerberos_pac");
        if (!mem_ctx) {
                goto out;
        }
 
-       opt_password = net_prompt_pass(opt_user_name);
+       c->opt_password = net_prompt_pass(c, c->opt_user_name);
 
        status = kerberos_return_pac(mem_ctx,
-                                    opt_user_name,
-                                    opt_password,
+                                    c->opt_user_name,
+                                    c->opt_password,
                                     0,
                                     NULL,
                                     NULL,
                                     NULL,
-                                    True,
-                                    True,
+                                    true,
+                                    true,
                                     2592000, /* one month */
                                     &pac);
        if (!NT_STATUS_IS_OK(status)) {
@@ -2568,7 +2309,9 @@ static int net_ads_kerberos_pac(int argc, const char **argv)
 
        info = get_logon_info_from_pac(pac);
        if (info) {
-               dump_pac_logon_info(0, info);
+               const char *s;
+               s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
+               d_printf("The Pac: %s\n", s);
        }
 
        ret = 0;
@@ -2577,27 +2320,34 @@ static int net_ads_kerberos_pac(int argc, const char **argv)
        return ret;
 }
 
-static int net_ads_kerberos_kinit(int argc, const char **argv)
+static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv)
 {
        TALLOC_CTX *mem_ctx = NULL;
        int ret = -1;
        NTSTATUS status;
 
+       if (c->display_usage) {
+               d_printf("Usage:\n"
+                        "net ads kerberos kinit\n"
+                        "    Get Ticket Granting Ticket (TGT) for the user\n");
+               return 0;
+       }
+
        mem_ctx = talloc_init("net_ads_kerberos_kinit");
        if (!mem_ctx) {
                goto out;
        }
 
-       opt_password = net_prompt_pass(opt_user_name);
+       c->opt_password = net_prompt_pass(c, c->opt_user_name);
 
-       ret = kerberos_kinit_password_ext(opt_user_name,
-                                         opt_password,
+       ret = kerberos_kinit_password_ext(c->opt_user_name,
+                                         c->opt_password,
                                          0,
                                          NULL,
                                          NULL,
                                          NULL,
-                                         True,
-                                         True,
+                                         true,
+                                         true,
                                          2592000, /* one month */
                                          &status);
        if (ret) {
@@ -2608,67 +2358,198 @@ static int net_ads_kerberos_kinit(int argc, const char **argv)
        return ret;
 }
 
-int net_ads_kerberos(int argc, const char **argv)
+int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"KINIT", net_ads_kerberos_kinit},
-               {"RENEW", net_ads_kerberos_renew},
-               {"PAC", net_ads_kerberos_pac},
-               {"HELP", net_ads_kerberos_usage},
-               {NULL, NULL}
+               {
+                       "kinit",
+                       net_ads_kerberos_kinit,
+                       NET_TRANSPORT_ADS,
+                       "Retrieve Ticket Granting Ticket (TGT)",
+                       "net ads kerberos kinit\n"
+                       "    Receive Ticket Granting Ticket (TGT)"
+               },
+               {
+                       "renew",
+                       net_ads_kerberos_renew,
+                       NET_TRANSPORT_ADS,
+                       "Renew Ticket Granting Ticket from credential cache"
+                       "net ads kerberos renew\n"
+                       "    Renew Ticket Granting Ticket from credential cache"
+               },
+               {
+                       "pac",
+                       net_ads_kerberos_pac,
+                       NET_TRANSPORT_ADS,
+                       "Dump Kerberos PAC",
+                       "net ads kerberos pac\n"
+                       "    Dump Kerberos PAC"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(argc, argv, func, net_ads_kerberos_usage);
+       return net_run_function(c, argc, argv, "net ads kerberos", func);
 }
 
-
-int net_ads_help(int argc, const char **argv)
+int net_ads(struct net_context *c, int argc, const char **argv)
 {
        struct functable func[] = {
-               {"USER", net_ads_user_usage},
-               {"GROUP", net_ads_group_usage},
-               {"PRINTER", net_ads_printer_usage},
-               {"SEARCH", net_ads_search_usage},
-               {"INFO", net_ads_info},
-               {"JOIN", net_ads_join_usage},
-               {"DNS", net_ads_dns_usage},
-               {"LEAVE", net_ads_leave},
-               {"STATUS", net_ads_status},
-               {"PASSWORD", net_ads_password},
-               {"CHANGETRUSTPW", net_ads_changetrustpw},
-               {NULL, NULL}
+               {
+                       "info",
+                       net_ads_info,
+                       NET_TRANSPORT_ADS,
+                       "Display details on remote ADS server",
+                       "net ads info\n"
+                       "    Display details on remote ADS server"
+               },
+               {
+                       "join",
+                       net_ads_join,
+                       NET_TRANSPORT_ADS,
+                       "Join the local machine to ADS realm",
+                       "net ads join\n"
+                       "    Join the local machine to ADS realm"
+               },
+               {
+                       "testjoin",
+                       net_ads_testjoin,
+                       NET_TRANSPORT_ADS,
+                       "Validate machine account",
+                       "net ads testjoin\n"
+                       "    Validate machine account"
+               },
+               {
+                       "leave",
+                       net_ads_leave,
+                       NET_TRANSPORT_ADS,
+                       "Remove the local machine from ADS",
+                       "net ads leave\n"
+                       "    Remove the local machine from ADS"
+               },
+               {
+                       "status",
+                       net_ads_status,
+                       NET_TRANSPORT_ADS,
+                       "Display machine account details",
+                       "net ads status\n"
+                       "    Display machine account details"
+               },
+               {
+                       "user",
+                       net_ads_user,
+                       NET_TRANSPORT_ADS,
+                       "List/modify users",
+                       "net ads user\n"
+                       "    List/modify users"
+               },
+               {
+                       "group",
+                       net_ads_group,
+                       NET_TRANSPORT_ADS,
+                       "List/modify groups",
+                       "net ads group\n"
+                       "    List/modify groups"
+               },
+               {
+                       "dns",
+                       net_ads_dns,
+                       NET_TRANSPORT_ADS,
+                       "Issue dynamic DNS update",
+                       "net ads dns\n"
+                       "    Issue dynamic DNS update"
+               },
+               {
+                       "password",
+                       net_ads_password,
+                       NET_TRANSPORT_ADS,
+                       "Change user passwords",
+                       "net ads password\n"
+                       "    Change user passwords"
+               },
+               {
+                       "changetrustpw",
+                       net_ads_changetrustpw,
+                       NET_TRANSPORT_ADS,
+                       "Change trust account password",
+                       "net ads changetrustpw\n"
+                       "    Change trust account password"
+               },
+               {
+                       "printer",
+                       net_ads_printer,
+                       NET_TRANSPORT_ADS,
+                       "List/modify printer entries",
+                       "net ads printer\n"
+                       "    List/modify printer entries"
+               },
+               {
+                       "search",
+                       net_ads_search,
+                       NET_TRANSPORT_ADS,
+                       "Issue LDAP search using filter",
+                       "net ads search\n"
+                       "    Issue LDAP search using filter"
+               },
+               {
+                       "dn",
+                       net_ads_dn,
+                       NET_TRANSPORT_ADS,
+                       "Issue LDAP search by DN",
+                       "net ads dn\n"
+                       "    Issue LDAP search by DN"
+               },
+               {
+                       "sid",
+                       net_ads_sid,
+                       NET_TRANSPORT_ADS,
+                       "Issue LDAP search by SID",
+                       "net ads sid\n"
+                       "    Issue LDAP search by SID"
+               },
+               {
+                       "workgroup",
+                       net_ads_workgroup,
+                       NET_TRANSPORT_ADS,
+                       "Display workgroup name",
+                       "net ads workgroup\n"
+                       "    Display the workgroup name"
+               },
+               {
+                       "lookup",
+                       net_ads_lookup,
+                       NET_TRANSPORT_ADS,
+                       "Perfom CLDAP query on DC",
+                       "net ads lookup\n"
+                       "    Find the ADS DC using CLDAP lookups"
+               },
+               {
+                       "keytab",
+                       net_ads_keytab,
+                       NET_TRANSPORT_ADS,
+                       "Manage local keytab file",
+                       "net ads keytab\n"
+                       "    Manage local keytab file"
+               },
+               {
+                       "gpo",
+                       net_ads_gpo,
+                       NET_TRANSPORT_ADS,
+                       "Manage group policy objects",
+                       "net ads gpo\n"
+                       "    Manage group policy objects"
+               },
+               {
+                       "kerberos",
+                       net_ads_kerberos,
+                       NET_TRANSPORT_ADS,
+                       "Manage kerberos keytab",
+                       "net ads kerberos\n"
+                       "    Manage kerberos keytab"
+               },
+               {NULL, NULL, 0, NULL, NULL}
        };
 
-       return net_run_function(argc, argv, func, net_ads_usage);
-}
-
-int net_ads(int argc, const char **argv)
-{
-       struct functable func[] = {
-               {"INFO", net_ads_info},
-               {"JOIN", net_ads_join},
-               {"TESTJOIN", net_ads_testjoin},
-               {"LEAVE", net_ads_leave},
-               {"STATUS", net_ads_status},
-               {"USER", net_ads_user},
-               {"GROUP", net_ads_group},
-               {"DNS", net_ads_dns},
-               {"PASSWORD", net_ads_password},
-               {"CHANGETRUSTPW", net_ads_changetrustpw},
-               {"PRINTER", net_ads_printer},
-               {"SEARCH", net_ads_search},
-               {"DN", net_ads_dn},
-               {"SID", net_ads_sid},
-               {"WORKGROUP", net_ads_workgroup},
-               {"LOOKUP", net_ads_lookup},
-               {"KEYTAB", net_ads_keytab},
-               {"GPO", net_ads_gpo},
-               {"KERBEROS", net_ads_kerberos},
-               {"HELP", net_ads_help},
-               {NULL, NULL}
-       };
-
-       return net_run_function(argc, argv, func, net_ads_usage);
+       return net_run_function(c, argc, argv, "net ads", func);
 }
 
 #else
@@ -2679,60 +2560,50 @@ static int net_ads_noads(void)
        return -1;
 }
 
-int net_ads_keytab(int argc, const char **argv)
-{
-       return net_ads_noads();
-}
-
-int net_ads_kerberos(int argc, const char **argv)
-{
-       return net_ads_noads();
-}
-
-int net_ads_usage(int argc, const char **argv)
+int net_ads_keytab(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
 }
 
-int net_ads_help(int argc, const char **argv)
+int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
 }
 
-int net_ads_changetrustpw(int argc, const char **argv)
+int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
 }
 
-int net_ads_join(int argc, const char **argv)
+int net_ads_join(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
 }
 
-int net_ads_user(int argc, const char **argv)
+int net_ads_user(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
 }
 
-int net_ads_group(int argc, const char **argv)
+int net_ads_group(struct net_context *c, int argc, const char **argv)
 {
        return net_ads_noads();
 }
 
 /* this one shouldn't display a message */
-int net_ads_check(void)
+int net_ads_check(struct net_context *c)
 {
        return -1;
 }
 
-int net_ads_check_our_domain(void)
+int net_ads_check_our_domain(struct net_context *c)
 {
        return -1;
 }
 
-int net_ads(int argc, const char **argv)
+int net_ads(struct net_context *c, int argc, const char **argv)
 {
-       return net_ads_usage(argc, argv);
+       return net_ads_noads();
 }
 
 #endif /* WITH_ADS */