Roadmap: Clarify that 4.0 is our next release
[Samba/bb.git] / dfs_server / dfs_server_ad.c
blobb7004c5506fb08104dce6d6ed6f5b6bfa4cc3c0b
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright Matthieu Patou <mat@matws.net> 2010-2011
5 Copyright Stefan Metzmacher 2011
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "librpc/gen_ndr/dfsblobs.h"
23 #include "librpc/gen_ndr/ndr_dfsblobs.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "auth/session.h"
26 #include "param/param.h"
27 #include "lib/tsocket/tsocket.h"
28 #include "dfs_server/dfs_server_ad.h"
30 #define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */
32 /* A DC set is a group of DC, they might have been grouped together
33 because they belong to the same site, or to site with same cost ...
35 struct dc_set {
36 const char **names;
37 uint32_t count;
41 fill a referral type structure
43 static NTSTATUS fill_normal_dfs_referraltype(TALLOC_CTX *mem_ctx,
44 struct dfs_referral_type *ref,
45 uint16_t version,
46 const char *dfs_path,
47 const char *server_path, int isfirstoffset)
49 ZERO_STRUCTP(ref);
50 switch (version) {
51 case 4:
52 ref->version = version;
53 /* For the moment there is a bug with XP that don't seems to appriciate much
54 * level4 so we return just level 3 for everyone
56 ref->referral.v4.server_type = DFS_SERVER_NON_ROOT;
57 /* "normal" referral seems to always include the GUID */
58 ref->referral.v4.size = 34;
60 if (isfirstoffset) {
61 ref->referral.v4.entry_flags = DFS_HEADER_FLAG_TARGET_BCK;
63 ref->referral.v4.ttl = 900; /* As w2k8r2 */
64 ref->referral.v4.referrals.r1.DFS_path = talloc_strdup(mem_ctx, dfs_path);
65 if (ref->referral.v4.referrals.r1.DFS_path == NULL) {
66 return NT_STATUS_NO_MEMORY;
68 ref->referral.v4.referrals.r1.DFS_alt_path = talloc_strdup(mem_ctx, dfs_path);
69 if (ref->referral.v4.referrals.r1.DFS_alt_path == NULL) {
70 return NT_STATUS_NO_MEMORY;
72 ref->referral.v4.referrals.r1.netw_address = talloc_strdup(mem_ctx, server_path);
73 if (ref->referral.v4.referrals.r1.netw_address == NULL) {
74 return NT_STATUS_NO_MEMORY;
76 return NT_STATUS_OK;
77 case 3:
78 ref->version = version;
79 ref->referral.v3.server_type = DFS_SERVER_NON_ROOT;
80 /* "normal" referral seems to always include the GUID */
81 ref->referral.v3.size = 34;
83 ref->referral.v3.entry_flags = 0;
84 ref->referral.v3.ttl = 600; /* As w2k3 */
85 ref->referral.v3.referrals.r1.DFS_path = talloc_strdup(mem_ctx, dfs_path);
86 if (ref->referral.v3.referrals.r1.DFS_path == NULL) {
87 return NT_STATUS_NO_MEMORY;
89 ref->referral.v3.referrals.r1.DFS_alt_path = talloc_strdup(mem_ctx, dfs_path);
90 if (ref->referral.v3.referrals.r1.DFS_alt_path == NULL) {
91 return NT_STATUS_NO_MEMORY;
93 ref->referral.v3.referrals.r1.netw_address = talloc_strdup(mem_ctx, server_path);
94 if (ref->referral.v3.referrals.r1.netw_address == NULL) {
95 return NT_STATUS_NO_MEMORY;
97 return NT_STATUS_OK;
99 return NT_STATUS_INVALID_LEVEL;
103 fill a domain refererral
105 static NTSTATUS fill_domain_dfs_referraltype(TALLOC_CTX *mem_ctx,
106 struct dfs_referral_type *ref,
107 uint16_t version,
108 const char *domain,
109 const char **names,
110 uint16_t numnames)
112 switch (version) {
113 case 3:
114 ZERO_STRUCTP(ref);
115 DEBUG(8, ("Called fill_domain_dfs_referraltype\n"));
116 ref->version = version;
117 ref->referral.v3.server_type = DFS_SERVER_NON_ROOT;
118 #if 0
119 /* We use to have variable size, on Windows 2008R2 it's the same
120 * and it seems that it gives better results so ... let's use the same
121 * size.
123 * Additional note: XP SP2 will ask for version 3 and SP3 for version 4.
126 * It's hard coded ... don't think it's a good way but the
127 * sizeof return not the correct values
129 * We have 18 if the GUID is not included 34 otherwise
131 if (numnames == 0) {
132 /* Windows return without the guid when returning domain list
134 ref->referral.v3.size = 18;
135 } else {
136 ref->referral.v3.size = 34;
138 #endif
139 /* As seen in w2k8r2 it always return the null GUID */
140 ref->referral.v3.size = 34;
141 ref->referral.v3.entry_flags = DFS_FLAG_REFERRAL_DOMAIN_RESP;
142 ref->referral.v3.ttl = 600; /* As w2k3 and w2k8r2*/
143 ref->referral.v3.referrals.r2.special_name = talloc_strdup(mem_ctx,
144 domain);
145 if (ref->referral.v3.referrals.r2.special_name == NULL) {
146 return NT_STATUS_NO_MEMORY;
148 ref->referral.v3.referrals.r2.nb_expanded_names = numnames;
149 /* Put the final terminator */
150 if (names) {
151 int i;
152 const char **names2 = talloc_array(mem_ctx, const char *,
153 numnames+1);
154 NT_STATUS_HAVE_NO_MEMORY(names2);
155 for (i = 0; i<numnames; i++) {
156 names2[i] = talloc_asprintf(names2, "\\%s", names[i]);
157 NT_STATUS_HAVE_NO_MEMORY(names2[i]);
159 names2[numnames] = NULL;
160 ref->referral.v3.referrals.r2.expanded_names = names2;
162 return NT_STATUS_OK;
164 return NT_STATUS_INVALID_LEVEL;
168 get the DCs list within a site
170 static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb,
171 struct ldb_dn *sitedn, struct dc_set *list,
172 bool dofqdn)
174 static const char *attrs[] = { "serverReference", NULL };
175 static const char *attrs2[] = { "dNSHostName", "sAMAccountName", NULL };
176 struct ldb_result *r;
177 unsigned int i;
178 int ret;
179 const char **dc_list;
181 ret = ldb_search(ldb, ctx, &r, sitedn, LDB_SCOPE_SUBTREE, attrs,
182 "(&(objectClass=server)(serverReference=*))");
183 if (ret != LDB_SUCCESS) {
184 DEBUG(2,(__location__ ": Failed to get list of servers - %s\n",
185 ldb_errstring(ldb)));
186 return NT_STATUS_INTERNAL_ERROR;
189 if (r->count == 0) {
190 /* none in this site */
191 talloc_free(r);
192 return NT_STATUS_OK;
196 * need to search for all server object to know the size of the array.
197 * Search all the object of class server in this site
199 dc_list = talloc_array(r, const char *, r->count);
200 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dc_list, r);
202 /* TODO put some random here in the order */
203 list->names = talloc_realloc(list, list->names, const char *, list->count + r->count);
204 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names, r);
206 for (i = 0; i<r->count; i++) {
207 struct ldb_dn *dn;
208 struct ldb_result *r2;
210 dn = ldb_msg_find_attr_as_dn(ldb, ctx, r->msgs[i], "serverReference");
211 if (!dn) {
212 return NT_STATUS_INTERNAL_ERROR;
215 ret = ldb_search(ldb, r, &r2, dn, LDB_SCOPE_BASE, attrs2, "(objectClass=computer)");
216 if (ret != LDB_SUCCESS) {
217 DEBUG(2,(__location__ ": Search for computer on %s failed - %s\n",
218 ldb_dn_get_linearized(dn), ldb_errstring(ldb)));
219 return NT_STATUS_INTERNAL_ERROR;
222 if (dofqdn) {
223 const char *dns = ldb_msg_find_attr_as_string(r2->msgs[0], "dNSHostName", NULL);
224 if (dns == NULL) {
225 DEBUG(2,(__location__ ": dNSHostName missing on %s\n",
226 ldb_dn_get_linearized(dn)));
227 talloc_free(r);
228 return NT_STATUS_INTERNAL_ERROR;
231 list->names[list->count] = talloc_strdup(list->names, dns);
232 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names[list->count], r);
233 } else {
234 char *tmp;
235 const char *acct = ldb_msg_find_attr_as_string(r2->msgs[0], "sAMAccountName", NULL);
236 if (acct == NULL) {
237 DEBUG(2,(__location__ ": sAMAccountName missing on %s\n",
238 ldb_dn_get_linearized(dn)));
239 talloc_free(r);
240 return NT_STATUS_INTERNAL_ERROR;
243 tmp = talloc_strdup(list->names, acct);
244 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tmp, r);
246 /* Netbios name is also the sAMAccountName for
247 computer but without the final $ */
248 tmp[strlen(tmp) - 1] = '\0';
249 list->names[list->count] = tmp;
251 list->count++;
252 talloc_free(r2);
255 talloc_free(r);
256 return NT_STATUS_OK;
261 get all DCs
263 static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb,
264 const char *searched_site, bool need_fqdn,
265 struct dc_set ***pset_list, uint32_t flags)
268 * Flags will be used later to indicate things like least-expensive
269 * or same-site options
271 const char *attrs_none[] = { NULL };
272 const char *attrs3[] = { "name", NULL };
273 struct ldb_dn *configdn, *sitedn, *dn, *sitescontainerdn;
274 struct ldb_result *r;
275 struct dc_set **set_list = NULL;
276 uint32_t i;
277 int ret;
278 uint32_t current_pos = 0;
279 NTSTATUS status;
280 TALLOC_CTX *subctx = talloc_new(ctx);
282 *pset_list = set_list = NULL;
284 subctx = talloc_new(ctx);
285 NT_STATUS_HAVE_NO_MEMORY(subctx);
287 configdn = ldb_get_config_basedn(ldb);
289 /* Let's search for the Site container */
290 ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs_none,
291 "(objectClass=sitesContainer)");
292 if (ret != LDB_SUCCESS) {
293 DEBUG(2,(__location__ ": Failed to find sitesContainer within %s - %s\n",
294 ldb_dn_get_linearized(configdn), ldb_errstring(ldb)));
295 talloc_free(subctx);
296 return NT_STATUS_INTERNAL_ERROR;
298 if (r->count > 1) {
299 DEBUG(2,(__location__ ": Expected 1 sitesContainer - found %u within %s\n",
300 r->count, ldb_dn_get_linearized(configdn)));
301 talloc_free(subctx);
302 return NT_STATUS_INTERNAL_ERROR;
305 sitescontainerdn = talloc_steal(subctx, r->msgs[0]->dn);
306 talloc_free(r);
309 * TODO: Here we should have a more subtle handling
310 * for the case "same-site"
312 ret = ldb_search(ldb, subctx, &r, sitescontainerdn, LDB_SCOPE_SUBTREE,
313 attrs_none, "(objectClass=server)");
314 if (ret != LDB_SUCCESS) {
315 DEBUG(2,(__location__ ": Failed to find servers within %s - %s\n",
316 ldb_dn_get_linearized(sitescontainerdn), ldb_errstring(ldb)));
317 talloc_free(subctx);
318 return NT_STATUS_INTERNAL_ERROR;
320 talloc_free(r);
322 if (searched_site != NULL && searched_site[0] != '\0') {
323 ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE,
324 attrs_none, "(&(name=%s)(objectClass=site))", searched_site);
325 if (ret != LDB_SUCCESS) {
326 talloc_free(subctx);
327 return NT_STATUS_FOOBAR;
328 } else if (r->count != 1) {
329 talloc_free(subctx);
330 return NT_STATUS_FOOBAR;
333 /* All of this was to get the DN of the searched_site */
334 sitedn = r->msgs[0]->dn;
336 set_list = talloc_realloc(subctx, set_list, struct dc_set *, current_pos+1);
337 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx);
339 set_list[current_pos] = talloc(set_list, struct dc_set);
340 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx);
342 set_list[current_pos]->names = NULL;
343 set_list[current_pos]->count = 0;
344 status = get_dcs_insite(subctx, ldb, sitedn,
345 set_list[current_pos], need_fqdn);
346 if (!NT_STATUS_IS_OK(status)) {
347 DEBUG(2,(__location__ ": Failed to get DC from site %s - %s\n",
348 ldb_dn_get_linearized(sitedn), nt_errstr(status)));
349 talloc_free(subctx);
350 return status;
352 talloc_free(r);
353 current_pos++;
356 /* Let's find all the sites */
357 ret = ldb_search(ldb, subctx, &r, configdn, LDB_SCOPE_SUBTREE, attrs3, "(objectClass=site)");
358 if (ret != LDB_SUCCESS) {
359 DEBUG(2,(__location__ ": Failed to find any site containers in %s\n",
360 ldb_dn_get_linearized(configdn)));
361 talloc_free(subctx);
362 return NT_STATUS_INTERNAL_DB_CORRUPTION;
366 * TODO:
367 * We should randomize the order in the main site,
368 * it's mostly needed for sysvol/netlogon referral.
369 * Depending of flag we either randomize order of the
370 * not "in the same site DCs"
371 * or we randomize by group of site that have the same cost
372 * In the long run we want to manipulate an array of site_set
373 * All the site in one set have the same cost (if least-expansive options is selected)
374 * and we will put all the dc related to 1 site set into 1 DCs set.
375 * Within a site set, site order has to be randomized
377 * But for the moment we just return the list of sites
379 if (r->count) {
381 * We will realloc + 2 because we will need one additional place
382 * for element at current_pos + 1 for the NULL element
384 set_list = talloc_realloc(subctx, set_list, struct dc_set *,
385 current_pos+2);
386 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx);
388 set_list[current_pos] = talloc(ctx, struct dc_set);
389 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx);
391 set_list[current_pos]->names = NULL;
392 set_list[current_pos]->count = 0;
394 set_list[current_pos+1] = NULL;
397 for (i=0; i<r->count; i++) {
398 const char *site_name = ldb_msg_find_attr_as_string(r->msgs[i], "name", NULL);
399 if (site_name == NULL) {
400 DEBUG(2,(__location__ ": Failed to find name attribute in %s\n",
401 ldb_dn_get_linearized(r->msgs[i]->dn)));
402 talloc_free(subctx);
403 return NT_STATUS_INTERNAL_DB_CORRUPTION;
406 if (searched_site == NULL ||
407 strcmp(searched_site, site_name) != 0) {
408 DEBUG(2,(__location__ ": Site: %s %s\n",
409 searched_site, site_name));
412 * Do all the site but the one of the client
413 * (because it has already been done ...)
415 dn = r->msgs[i]->dn;
417 status = get_dcs_insite(subctx, ldb, dn,
418 set_list[current_pos],
419 need_fqdn);
420 if (!NT_STATUS_IS_OK(status)) {
421 talloc_free(subctx);
422 return status;
426 current_pos++;
427 set_list[current_pos] = NULL;
429 *pset_list = talloc_move(ctx, &set_list);
430 talloc_free(subctx);
431 return NT_STATUS_OK;
434 static NTSTATUS dodomain_referral(struct loadparm_context *lp_ctx,
435 struct ldb_context *sam_ctx,
436 const struct tsocket_address *client,
437 struct dfs_GetDFSReferral *r)
440 * TODO for the moment we just return the local domain
442 NTSTATUS status;
443 const char *dns_domain = lpcfg_dnsdomain(lp_ctx);
444 const char *netbios_domain = lpcfg_workgroup(lp_ctx);
445 struct dfs_referral_type *referrals;
446 const char *referral_str;
447 /* In the future this needs to be fetched from the ldb */
448 uint32_t found_domain = 2;
450 if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
451 DEBUG(10 ,("Received a domain referral request on a non DC\n"));
452 return NT_STATUS_INVALID_PARAMETER;
455 if (r->in.req.max_referral_level < 3) {
456 DEBUG(2,("invalid max_referral_level %u\n",
457 r->in.req.max_referral_level));
458 return NT_STATUS_UNSUCCESSFUL;
461 r->out.resp = talloc_zero(r, struct dfs_referral_resp);
462 if (r->out.resp == NULL) {
463 return NT_STATUS_NO_MEMORY;
466 r->out.resp->path_consumed = 0;
467 r->out.resp->header_flags = 0; /* Do like w2k3 */
468 r->out.resp->nb_referrals = found_domain; /* the fqdn one + the NT domain */
470 referrals = talloc_zero_array(r->out.resp,
471 struct dfs_referral_type,
472 r->out.resp->nb_referrals);
473 if (referrals == NULL) {
474 return NT_STATUS_NO_MEMORY;
476 r->out.resp->referral_entries = referrals;
478 referral_str = talloc_asprintf(r, "\\%s", netbios_domain);
479 if (referral_str == NULL) {
480 return NT_STATUS_NO_MEMORY;
483 status = fill_domain_dfs_referraltype(referrals,
484 &referrals[0], 3,
485 referral_str,
486 NULL, 0);
487 if (!NT_STATUS_IS_OK(status)) {
488 DEBUG(2,("%s: Unable to fill domain referral structure - %s\n",
489 __location__, nt_errstr(status)));
490 return status;
493 referral_str = talloc_asprintf(r, "\\%s", dns_domain);
494 if (referral_str == NULL) {
495 return NT_STATUS_NO_MEMORY;
498 status = fill_domain_dfs_referraltype(referrals,
499 &referrals[1], 3,
500 referral_str,
501 NULL, 0);
502 if (!NT_STATUS_IS_OK(status)) {
503 DEBUG(2,("%s: Unable to fill domain referral structure - %s\n",
504 __location__, nt_errstr(status)));
505 return status;
508 return NT_STATUS_OK;
512 * Handle the logic for dfs referral request like
513 * \\dns_domain or \\netbios_domain.
515 static NTSTATUS dodc_referral(struct loadparm_context *lp_ctx,
516 struct ldb_context *sam_ctx,
517 const struct tsocket_address *client,
518 struct dfs_GetDFSReferral *r,
519 const char *domain_name)
521 NTSTATUS status;
522 const char *site_name = NULL; /* Name of the site where the client is */
523 bool need_fqdn = false;
524 unsigned int i;
525 const char **dc_list = NULL;
526 uint32_t num_dcs = 0;
527 struct dc_set **set;
528 char *client_str = NULL;
529 struct dfs_referral_type *referrals;
530 const char *referral_str;
532 if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
533 return NT_STATUS_INVALID_PARAMETER;
536 if (r->in.req.max_referral_level < 3) {
537 DEBUG(2,("invalid max_referral_level %u\n",
538 r->in.req.max_referral_level));
539 return NT_STATUS_UNSUCCESSFUL;
542 DEBUG(10, ("in this we have request for %s requested is %s\n",
543 domain_name, r->in.req.servername));
545 if (strchr(domain_name,'.')) {
546 need_fqdn = 1;
549 if (tsocket_address_is_inet(client, "ip")) {
550 client_str = tsocket_address_inet_addr_string(client, r);
551 if (client_str == NULL) {
552 return NT_STATUS_NO_MEMORY;
556 site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL);
558 status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0);
559 if (!NT_STATUS_IS_OK(status)) {
560 DEBUG(3,("Unable to get list of DCs - %s\n",
561 nt_errstr(status)));
562 return status;
565 for(i=0; set[i]; i++) {
566 uint32_t j;
568 dc_list = talloc_realloc(r, dc_list, const char*,
569 num_dcs + set[i]->count + 1);
570 if (dc_list == NULL) {
571 return NT_STATUS_NO_MEMORY;
574 for(j=0; j<set[i]->count; j++) {
575 dc_list[num_dcs + j] = talloc_move(dc_list,
576 &set[i]->names[j]);
578 num_dcs = num_dcs + set[i]->count;
579 TALLOC_FREE(set[i]);
580 dc_list[num_dcs] = NULL;
583 r->out.resp = talloc_zero(r, struct dfs_referral_resp);
584 if (r->out.resp == NULL) {
585 return NT_STATUS_NO_MEMORY;
588 r->out.resp->path_consumed = 0;
589 r->out.resp->header_flags = 0; /* Do like w2k3 */
590 r->out.resp->nb_referrals = 1;
592 referrals = talloc_zero_array(r->out.resp,
593 struct dfs_referral_type,
594 r->out.resp->nb_referrals);
595 if (referrals == NULL) {
596 return NT_STATUS_NO_MEMORY;
598 r->out.resp->referral_entries = referrals;
600 if (r->in.req.servername[0] == '\\') {
601 referral_str = talloc_asprintf(referrals, "%s",
602 domain_name);
603 } else {
604 referral_str = talloc_asprintf(referrals, "\\%s",
605 domain_name);
607 if (referral_str == NULL) {
608 return NT_STATUS_NO_MEMORY;
611 status = fill_domain_dfs_referraltype(referrals,
612 &referrals[0], 3,
613 referral_str,
614 dc_list, num_dcs);
615 if (!NT_STATUS_IS_OK(status)) {
616 DEBUG(2,("%s: Unable to fill domain referral structure - %s\n",
617 __location__, nt_errstr(status)));
618 return status;
621 return NT_STATUS_OK;
625 * Handle the logic for dfs referral request like
626 * \\domain\sysvol or \\domain\netlogon
628 static NTSTATUS dosysvol_referral(struct loadparm_context *lp_ctx,
629 struct ldb_context *sam_ctx,
630 const struct tsocket_address *client,
631 struct dfs_GetDFSReferral *r,
632 const char *domain_name,
633 const char *dfs_name)
635 const char *site_name = NULL; /* Name of the site where the client is */
636 bool need_fqdn = false;
637 unsigned int i, c = 0, nb_entries = 0;
638 struct dc_set **set;
639 char *client_str = NULL;
640 NTSTATUS status;
641 struct dfs_referral_type *referrals;
643 if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
644 return NT_STATUS_INVALID_PARAMETER;
647 if (r->in.req.max_referral_level < 3) {
648 DEBUG(2,("invalid max_referral_level %u\n",
649 r->in.req.max_referral_level));
650 return NT_STATUS_UNSUCCESSFUL;
653 DEBUG(10, ("in this we have request for %s and share %s requested is %s\n",
654 domain_name, dfs_name, r->in.req.servername));
656 if (strchr(domain_name,'.')) {
657 need_fqdn = 1;
660 if (tsocket_address_is_inet(client, "ip")) {
661 client_str = tsocket_address_inet_addr_string(client, r);
662 if (client_str == NULL) {
663 return NT_STATUS_NO_MEMORY;
667 site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL);
669 status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0);
670 if (!NT_STATUS_IS_OK(status)) {
671 DEBUG(3,("Unable to get list of DCs - %s\n",
672 nt_errstr(status)));
673 return status;
676 for(i=0; set[i]; i++) {
677 nb_entries = nb_entries + set[i]->count;
680 r->out.resp = talloc_zero(r, struct dfs_referral_resp);
681 if (r->out.resp == NULL) {
682 return NT_STATUS_NO_MEMORY;
685 /* The length is expected in bytes */
686 r->out.resp->path_consumed = strlen_m(r->in.req.servername) * 2;
687 /* Do like w2k3 and like in 3.3.5.3 of MS-DFSC*/
688 r->out.resp->header_flags = DFS_HEADER_FLAG_STORAGE_SVR;
689 r->out.resp->nb_referrals = nb_entries;
691 referrals = talloc_zero_array(r->out.resp,
692 struct dfs_referral_type,
693 r->out.resp->nb_referrals);
694 if (referrals == NULL) {
695 return NT_STATUS_NO_MEMORY;
697 r->out.resp->referral_entries = referrals;
699 c = 0;
700 for(i=0; set[i]; i++) {
701 uint32_t j;
703 for(j=0; j< set[i]->count; j++) {
704 struct dfs_referral_type *ref = &referrals[c];
705 const char *referral_str;
707 referral_str = talloc_asprintf(referrals, "\\%s\\%s",
708 set[i]->names[j], dfs_name);
709 if (referral_str == NULL) {
710 return NT_STATUS_NO_MEMORY;
713 DEBUG(8,("Doing a dfs referral for %s with this value "
714 "%s requested %s\n",
715 set[i]->names[j], referral_str,
716 r->in.req.servername));
718 status = fill_normal_dfs_referraltype(referrals, ref,
719 r->in.req.max_referral_level,
720 r->in.req.servername,
721 referral_str, c==0);
724 if (!NT_STATUS_IS_OK(status)) {
725 DEBUG(2,("%s: Unable to fill domain referral "
726 "structure - %s\n",
727 __location__, nt_errstr(status)));
728 return status;
731 c++;
735 return NT_STATUS_OK;
739 trans2 getdfsreferral implementation
741 NTSTATUS dfs_server_ad_get_referrals(struct loadparm_context *lp_ctx,
742 struct ldb_context *sam_ctx,
743 const struct tsocket_address *client,
744 struct dfs_GetDFSReferral *r)
746 char *server_name = NULL;
747 char *dfs_name = NULL;
748 char *link_path = NULL;
749 const char *netbios_domain;
750 const char *dns_domain;
751 const char *netbios_name;
752 const char *dns_name;
754 if (!lpcfg_host_msdfs(lp_ctx)) {
755 return NT_STATUS_FS_DRIVER_REQUIRED;
758 if (r->in.req.servername == NULL) {
759 return NT_STATUS_INVALID_PARAMETER;
762 DEBUG(8, ("Requested DFS name: %s length: %u\n",
763 r->in.req.servername,
764 (unsigned int)strlen_m(r->in.req.servername)*2));
767 * If the servername is "" then we are in a case of domain dfs
768 * and the client just searches for the list of local domain
769 * it is attached and also trusted ones.
771 if (strlen(r->in.req.servername) == 0) {
772 return dodomain_referral(lp_ctx, sam_ctx, client, r);
775 server_name = talloc_strdup(r, r->in.req.servername);
776 if (server_name == NULL) {
777 return NT_STATUS_NO_MEMORY;
780 while(*server_name && *server_name == '\\') {
781 server_name++;
784 dfs_name = strchr(server_name, '\\');
785 if (dfs_name != NULL) {
786 dfs_name[0] = '\0';
787 dfs_name++;
789 link_path = strchr(dfs_name, '\\');
790 if (link_path != NULL) {
791 link_path[0] = '\0';
792 link_path++;
796 if (link_path != NULL) {
798 * If it is a DFS Link we do not
799 * handle it here.
801 return NT_STATUS_NOT_FOUND;
804 netbios_domain = lpcfg_workgroup(lp_ctx);
805 dns_domain = lpcfg_dnsdomain(lp_ctx);
806 netbios_name = lpcfg_netbios_name(lp_ctx);
807 dns_name = talloc_asprintf(r, "%s.%s", netbios_name, dns_domain);
808 if (dns_name == NULL) {
809 return NT_STATUS_NO_MEMORY;
812 if ((strcasecmp_m(server_name, netbios_name) == 0) ||
813 (strcasecmp_m(server_name, dns_name) == 0)) {
815 * If it is not domain related do not
816 * handle it here.
818 return NT_STATUS_NOT_FOUND;
822 if ((strcasecmp_m(server_name, netbios_domain) != 0) &&
823 (strcasecmp_m(server_name, dns_domain) != 0)) {
825 * Not a domain we handle.
827 return NT_STATUS_INVALID_PARAMETER;
831 * Here we have filtered the thing the requested name don't contain our DNS name.
832 * So if the share == NULL or if share in ("sysvol", "netlogon")
833 * then we proceed. In the first case it will be a dc refereal in the second it will
834 * be just a sysvol/netlogon referral.
836 if (dfs_name == NULL) {
837 return dodc_referral(lp_ctx, sam_ctx,
838 client, r, server_name);
842 * Here we have filtered the thing the requested name don't contain our DNS name.
843 * So if the share == NULL or if share in ("sysvol", "netlogon")
844 * then we proceed. In the first case it will be a dc refereal in the second it will
845 * be just a sysvol/netlogon referral.
847 if (strcasecmp(dfs_name, "sysvol") == 0 ||
848 strcasecmp(dfs_name, "netlogon") == 0) {
849 return dosysvol_referral(lp_ctx, sam_ctx, client, r,
850 server_name, dfs_name);
853 /* By default until all the case are handled */
854 return NT_STATUS_NOT_FOUND;