*/
#include "includes.h"
+#include "libads/sitename_cache.h"
+#include "../libcli/netlogon.h"
/* nmbd.c sets this to True. */
bool global_in_nmbd = False;
****************************************************************************/
#define SAFKEY_FMT "SAF/DOMAIN/%s"
#define SAF_TTL 900
+#define SAFJOINKEY_FMT "SAFJOIN/DOMAIN/%s"
+#define SAFJOIN_TTL 3600
static char *saf_key(const char *domain)
{
return keystr;
}
+static char *saf_join_key(const char *domain)
+{
+ char *keystr;
+
+ asprintf_strupper_m(&keystr, SAFJOINKEY_FMT, domain);
+
+ return keystr;
+}
+
/****************************************************************************
****************************************************************************/
return False;
}
- if ( !gencache_init() )
- return False;
-
key = saf_key( domain );
- expire = time( NULL ) + SAF_TTL;
+ expire = time( NULL ) + lp_parm_int(-1, "saf","ttl", SAF_TTL);
DEBUG(10,("saf_store: domain = [%s], server = [%s], expire = [%u]\n",
domain, servername, (unsigned int)expire ));
return ret;
}
+bool saf_join_store( const char *domain, const char *servername )
+{
+ char *key;
+ time_t expire;
+ bool ret = False;
+
+ if ( !domain || !servername ) {
+ DEBUG(2,("saf_join_store: Refusing to store empty domain or servername!\n"));
+ return False;
+ }
+
+ if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) {
+ DEBUG(0,("saf_join_store: refusing to store 0 length domain or servername!\n"));
+ return False;
+ }
+
+ key = saf_join_key( domain );
+ expire = time( NULL ) + lp_parm_int(-1, "saf","join ttl", SAFJOIN_TTL);
+
+ DEBUG(10,("saf_join_store: domain = [%s], server = [%s], expire = [%u]\n",
+ domain, servername, (unsigned int)expire ));
+
+ ret = gencache_set( key, servername, expire );
+
+ SAFE_FREE( key );
+
+ return ret;
+}
+
bool saf_delete( const char *domain )
{
char *key;
return False;
}
- if ( !gencache_init() )
- return False;
+ key = saf_join_key(domain);
+ ret = gencache_del(key);
+ SAFE_FREE(key);
+
+ if (ret) {
+ DEBUG(10,("saf_delete[join]: domain = [%s]\n", domain ));
+ }
key = saf_key(domain);
ret = gencache_del(key);
+ SAFE_FREE(key);
if (ret) {
DEBUG(10,("saf_delete: domain = [%s]\n", domain ));
}
- SAFE_FREE( key );
-
return ret;
}
return NULL;
}
- if ( !gencache_init() )
- return False;
+ key = saf_join_key( domain );
+
+ ret = gencache_get( key, &server, &timeout );
+
+ SAFE_FREE( key );
+
+ if ( ret ) {
+ DEBUG(5,("saf_fetch[join]: Returning \"%s\" for \"%s\" domain\n",
+ server, domain ));
+ return server;
+ }
key = saf_key( domain );
p.ip = ((const struct sockaddr_in *)to_ss)->sin_addr;
p.port = NMB_PORT;
- p.fd = fd;
+ p.recv_fd = -1;
+ p.send_fd = fd;
p.timestamp = time(NULL);
p.packet_type = NMB_PACKET;
if (!interpret_string_addr(&ss, lp_socket_address(),
AI_NUMERICHOST|AI_PASSIVE)) {
- zero_addr(&ss);
+ zero_sockaddr(&ss);
}
sock = open_socket_in(SOCK_DGRAM, 0, 3, &ss, True);
goto done;
for (i=0;i<count;i++) {
- if (status[i].type == type)
+ /* Find first one of the requested type that's not a GROUP. */
+ if (status[i].type == type && ! (status[i].flags & 0x80))
break;
}
if (i == count)
*/
static int addr_compare(const struct sockaddr_storage *ss1,
- const struct sockaddr_storage *ss2)
+ const struct sockaddr_storage *ss2)
{
int max_bits1=0, max_bits2=0;
int num_interfaces = iface_count();
int i;
- /* Sort IPv6 addresses first. */
+ /* Sort IPv4 addresses first. */
if (ss1->ss_family != ss2->ss_family) {
if (ss2->ss_family == AF_INET) {
- return -1;
- } else {
return 1;
+ } else {
+ return -1;
}
}
}
/* Bias towards directly reachable IPs */
- if (iface_local(ss1)) {
+ if (iface_local((struct sockaddr *)ss1)) {
if (ss1->ss_family == AF_INET) {
max_bits1 += 32;
} else {
max_bits1 += 128;
}
}
- if (iface_local(ss2)) {
+ if (iface_local((struct sockaddr *)ss2)) {
if (ss2->ss_family == AF_INET) {
max_bits2 += 32;
} else {
return;
}
- qsort(sslist, count, sizeof(struct sockaddr_storage),
- QSORT_CAST addr_compare);
+ TYPESAFE_QSORT(sslist, count, addr_compare);
}
static void sort_service_list(struct ip_service *servlist, int count)
return;
}
- qsort(servlist, count, sizeof(struct ip_service),
- QSORT_CAST ip_service_compare);
+ TYPESAFE_QSORT(servlist, count, ip_service_compare);
}
/**********************************************************************
/* one loop to remove duplicates */
for ( i=0; i<count; i++ ) {
- if ( is_zero_addr(&iplist[i].ss)) {
+ if ( is_zero_addr((struct sockaddr *)&iplist[i].ss)) {
continue;
}
for ( j=i+1; j<count; j++ ) {
- if (addr_equal(&iplist[i].ss, &iplist[j].ss) &&
+ if (sockaddr_equal((struct sockaddr *)&iplist[i].ss, (struct sockaddr *)&iplist[j].ss) &&
iplist[i].port == iplist[j].port) {
- zero_addr(&iplist[j].ss);
+ zero_sockaddr(&iplist[j].ss);
}
}
}
/* one loop to clean up any holes we left */
/* first ip should never be a zero_ip() */
for (i = 0; i<count; ) {
- if (is_zero_addr(&iplist[i].ss) ) {
+ if (is_zero_addr((struct sockaddr *)&iplist[i].ss) ) {
if (i != count-1) {
memmove(&iplist[i], &iplist[i+1],
(count - i - 1)*sizeof(iplist[i]));
return count;
}
+static bool prioritize_ipv4_list(struct ip_service *iplist, int count)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct ip_service *iplist_new = TALLOC_ARRAY(frame, struct ip_service, count);
+ int i, j;
+
+ if (iplist_new == NULL) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ j = 0;
+
+ /* Copy IPv4 first. */
+ for (i = 0; i < count; i++) {
+ if (iplist[i].ss.ss_family == AF_INET) {
+ iplist_new[j++] = iplist[i];
+ }
+ }
+
+ /* Copy IPv6. */
+ for (i = 0; i < count; i++) {
+ if (iplist[i].ss.ss_family != AF_INET) {
+ iplist_new[j++] = iplist[i];
+ }
+ }
+
+ memcpy(iplist, iplist_new, sizeof(struct ip_service)*count);
+ TALLOC_FREE(frame);
+ return true;
+}
+
/****************************************************************************
Do a netbios name query to find someones IP.
Returns an array of IP addresses or NULL if none.
p.ip = ((struct sockaddr_in *)to_ss)->sin_addr;
p.port = NMB_PORT;
- p.fd = fd;
+ p.recv_fd = -1;
+ p.send_fd = fd;
p.timestamp = time(NULL);
p.packet_type = NMB_PACKET;
return ss_list;
}
-/********************************************************
- Start parsing the lmhosts file.
-*********************************************************/
-
-XFILE *startlmhosts(const char *fname)
-{
- XFILE *fp = x_fopen(fname,O_RDONLY, 0);
- if (!fp) {
- DEBUG(4,("startlmhosts: Can't open lmhosts file %s. "
- "Error was %s\n",
- fname, strerror(errno)));
- return NULL;
- }
- return fp;
-}
-
-/********************************************************
- Parse the next line in the lmhosts file.
-*********************************************************/
-
-bool getlmhostsent(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, int *name_type,
- struct sockaddr_storage *pss)
-{
- char line[1024];
-
- *pp_name = NULL;
-
- while(!x_feof(fp) && !x_ferror(fp)) {
- char ip[INET6_ADDRSTRLEN];
- fstring flags;
- fstring extra;
- fstring name;
- const char *ptr;
- char *ptr1;
- int count = 0;
-
- *name_type = -1;
-
- if (!fgets_slash(line,sizeof(line),fp)) {
- continue;
- }
-
- if (*line == '#') {
- continue;
- }
-
- ip[0] = '\0';
- name[0] = '\0';
- flags[0] = '\0';
-
- ptr = line;
-
- if (next_token(&ptr,ip ,NULL,sizeof(ip)))
- ++count;
- if (next_token(&ptr,name ,NULL, sizeof(name)))
- ++count;
- if (next_token(&ptr,flags,NULL, sizeof(flags)))
- ++count;
- if (next_token(&ptr,extra,NULL, sizeof(extra)))
- ++count;
-
- if (count <= 0)
- continue;
-
- if (count > 0 && count < 2) {
- DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",
- line));
- continue;
- }
-
- if (count >= 4) {
- DEBUG(0,("getlmhostsent: too many columns "
- "in lmhosts file (obsolete syntax)\n"));
- continue;
- }
-
- DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n",
- ip, name, flags));
-
- if (strchr_m(flags,'G') || strchr_m(flags,'S')) {
- DEBUG(0,("getlmhostsent: group flag "
- "in lmhosts ignored (obsolete)\n"));
- continue;
- }
-
- if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) {
- DEBUG(0,("getlmhostsent: invalid address "
- "%s.\n", ip));
- }
-
- /* Extra feature. If the name ends in '#XX',
- * where XX is a hex number, then only add that name type. */
- if((ptr1 = strchr_m(name, '#')) != NULL) {
- char *endptr;
- ptr1++;
-
- *name_type = (int)strtol(ptr1, &endptr, 16);
- if(!*ptr1 || (endptr == ptr1)) {
- DEBUG(0,("getlmhostsent: invalid name "
- "%s containing '#'.\n", name));
- continue;
- }
-
- *(--ptr1) = '\0'; /* Truncate at the '#' */
- }
-
- *pp_name = talloc_strdup(ctx, name);
- if (!*pp_name) {
- return false;
- }
- return true;
- }
-
- return false;
-}
-
-/********************************************************
- Finish parsing the lmhosts file.
-*********************************************************/
-
-void endlmhosts(XFILE *fp)
-{
- x_fclose(fp);
-}
-
/********************************************************
convert an array if struct sockaddr_storage to struct ip_service
return false on failure. Port is set to PORT_NONE;
if (!interpret_string_addr(&ss, lp_socket_address(),
AI_NUMERICHOST|AI_PASSIVE)) {
- zero_addr(&ss);
+ zero_sockaddr(&ss);
}
sock = open_socket_in( SOCK_DGRAM, 0, 3, &ss, true );
/* the address we will be sending from */
if (!interpret_string_addr(&src_ss, lp_socket_address(),
AI_NUMERICHOST|AI_PASSIVE)) {
- zero_addr(&src_ss);
+ zero_sockaddr(&src_ss);
}
if (src_ss.ss_family != AF_INET) {
DEBUG(3,("resolve_wins: cannot receive WINS replies "
"on IPv6 address %s\n",
addr));
+ wins_srv_tags_free(wins_tags);
return NT_STATUS_INVALID_PARAMETER;
}
/*
* "lmhosts" means parse the local lmhosts file.
*/
-
- XFILE *fp;
- char *lmhost_name = NULL;
- int name_type2;
- struct sockaddr_storage return_ss;
+ struct sockaddr_storage *ss_list;
NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
TALLOC_CTX *ctx = NULL;
"Attempting lmhosts lookup for name %s<0x%x>\n",
name, name_type));
- fp = startlmhosts(dyn_LMHOSTSFILE);
-
- if ( fp == NULL )
- return NT_STATUS_NO_SUCH_FILE;
-
ctx = talloc_init("resolve_lmhosts");
if (!ctx) {
- endlmhosts(fp);
return NT_STATUS_NO_MEMORY;
}
- while (getlmhostsent(ctx, fp, &lmhost_name, &name_type2, &return_ss)) {
-
- if (!strequal(name, lmhost_name)) {
- TALLOC_FREE(lmhost_name);
- continue;
- }
-
- if ((name_type2 != -1) && (name_type != name_type2)) {
- TALLOC_FREE(lmhost_name);
- continue;
- }
-
- *return_iplist = SMB_REALLOC_ARRAY((*return_iplist),
- struct ip_service,
- (*return_count)+1);
-
- if ((*return_iplist) == NULL) {
- TALLOC_FREE(ctx);
- endlmhosts(fp);
- DEBUG(3,("resolve_lmhosts: malloc fail !\n"));
+ status = resolve_lmhosts_file_as_sockaddr(get_dyn_LMHOSTSFILE(),
+ name, name_type,
+ ctx,
+ &ss_list,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ if (convert_ss2service(return_iplist,
+ ss_list,
+ *return_count)) {
+ talloc_free(ctx);
+ return NT_STATUS_OK;
+ } else {
+ talloc_free(ctx);
return NT_STATUS_NO_MEMORY;
}
-
- (*return_iplist)[*return_count].ss = return_ss;
- (*return_iplist)[*return_count].port = PORT_NONE;
- *return_count += 1;
-
- /* we found something */
- status = NT_STATUS_OK;
-
- /* Multiple names only for DC lookup */
- if (name_type != 0x1c)
- break;
}
-
- TALLOC_FREE(ctx);
- endlmhosts(fp);
+ talloc_free(ctx);
return status;
}
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
+#if !defined(HAVE_IPV6)
+ /* Unless we have IPv6, we really only want IPv4 addresses back. */
+ hints.ai_family = AF_INET;
+#endif
+
ret = getaddrinfo(name,
NULL,
&hints,
continue;
}
- memset(&ss, '\0', sizeof(ss));
+ ZERO_STRUCT(ss);
memcpy(&ss, res->ai_addr, res->ai_addrlen);
*return_count += 1;
Resolve via "ADS" method.
*********************************************************/
-NTSTATUS resolve_ads(const char *name,
- int name_type,
- const char *sitename,
- struct ip_service **return_iplist,
- int *return_count)
+static NTSTATUS resolve_ads(const char *name,
+ int name_type,
+ const char *sitename,
+ struct ip_service **return_iplist,
+ int *return_count)
{
int i, j;
NTSTATUS status;
* for falling back to netbios lookups is that our DNS server
* doesn't know anything about the DC's -- jerry */
- if (!is_zero_addr(&r->ss)) {
+ if (!is_zero_addr((struct sockaddr *)&r->ss)) {
(*return_count)++;
}
}
**********************************************************************/
NTSTATUS internal_resolve_name(const char *name,
- int name_type,
+ int name_type,
const char *sitename,
struct ip_service **return_iplist,
int *return_count,
const char *resolve_order)
{
- const char *name_resolve_list;
- fstring tok;
+ char *tok;
const char *ptr;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
int i;
+ TALLOC_CTX *frame = NULL;
*return_iplist = NULL;
*return_count = 0;
DEBUG(10, ("internal_resolve_name: looking up %s#%x (sitename %s)\n",
- name, name_type, sitename ? sitename : NULL));
+ name, name_type, sitename ? sitename : "(null)"));
if (is_ipaddress(name)) {
if ((*return_iplist = SMB_MALLOC_P(struct ip_service)) ==
return NT_STATUS_INVALID_PARAMETER;
}
- if (!resolve_order) {
- name_resolve_list = lp_name_resolve_order();
- } else {
- name_resolve_list = resolve_order;
- }
-
- if (!name_resolve_list[0]) {
+ if (!resolve_order[0]) {
ptr = "host";
} else {
- ptr = name_resolve_list;
+ ptr = resolve_order;
}
/* iterate through the name resolution backends */
- while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
+ frame = talloc_stackframe();
+ while (next_token_talloc(frame, &ptr, &tok, LIST_SEP)) {
if((strequal(tok, "host") || strequal(tok, "hosts"))) {
status = resolve_hosts(name, name_type, return_iplist,
return_count);
/* All of the resolve_* functions above have returned false. */
+ TALLOC_FREE(frame);
SAFE_FREE(*return_iplist);
*return_count = 0;
DEBUG(10, ("\n"));
}
+ TALLOC_FREE(frame);
return status;
}
bool resolve_name(const char *name,
struct sockaddr_storage *return_ss,
- int name_type)
+ int name_type,
+ bool prefer_ipv4)
{
struct ip_service *ss_list = NULL;
char *sitename = NULL;
lp_name_resolve_order()))) {
int i;
+ if (prefer_ipv4) {
+ for (i=0; i<count; i++) {
+ if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
+ !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss) &&
+ (ss_list[i].ss.ss_family == AF_INET)) {
+ *return_ss = ss_list[i].ss;
+ SAFE_FREE(ss_list);
+ SAFE_FREE(sitename);
+ return True;
+ }
+ }
+ }
+
/* only return valid addresses for TCP connections */
for (i=0; i<count; i++) {
- if (!is_zero_addr(&ss_list[i].ss) &&
- !is_broadcast_addr(&ss_list[i].ss)) {
+ if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
+ !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss)) {
*return_ss = ss_list[i].ss;
SAFE_FREE(ss_list);
SAFE_FREE(sitename);
return False;
}
+/********************************************************
+ Internal interface to resolve a name into a list of IP addresses.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_ss_arr,
+ unsigned int *p_num_entries)
+{
+ struct ip_service *ss_list = NULL;
+ char *sitename = NULL;
+ int count = 0;
+ int i;
+ unsigned int num_entries;
+ NTSTATUS status;
+
+ *p_num_entries = 0;
+ *return_ss_arr = NULL;
+
+ if (is_ipaddress(name)) {
+ *return_ss_arr = TALLOC_P(ctx, struct sockaddr_storage);
+ if (!*return_ss_arr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!interpret_string_addr(*return_ss_arr, name, AI_NUMERICHOST)) {
+ TALLOC_FREE(*return_ss_arr);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+ *p_num_entries = 1;
+ return NT_STATUS_OK;
+ }
+
+ sitename = sitename_fetch(lp_realm()); /* wild guess */
+
+ status = internal_resolve_name(name, name_type, sitename,
+ &ss_list, &count,
+ lp_name_resolve_order());
+ SAFE_FREE(sitename);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* only return valid addresses for TCP connections */
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
+ !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss)) {
+ num_entries++;
+ }
+ }
+ if (num_entries == 0) {
+ SAFE_FREE(ss_list);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ *return_ss_arr = TALLOC_ARRAY(ctx,
+ struct sockaddr_storage,
+ num_entries);
+ if (!(*return_ss_arr)) {
+ SAFE_FREE(ss_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
+ !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss)) {
+ (*return_ss_arr)[num_entries++] = ss_list[i].ss;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ *p_num_entries = num_entries;
+
+ SAFE_FREE(ss_list);
+ return NT_STATUS_OK;
+}
+
/********************************************************
Find the IP address of the master browser or DMB for a workgroup.
*********************************************************/
const char *p;
char *port_str = NULL;
int port;
- fstring name;
+ char *name;
int num_addresses = 0;
int local_count, i, j;
struct ip_service *return_iplist = NULL;
NTSTATUS status;
TALLOC_CTX *ctx = talloc_init("get_dc_list");
+ *ip_list = NULL;
+ *count = 0;
+
if (!ctx) {
return NT_STATUS_NO_MEMORY;
}
*/
p = pserver;
- while (next_token(&p,name,LIST_SEP,sizeof(name))) {
- if (strequal(name, "*")) {
+ while (next_token_talloc(ctx, &p, &name, LIST_SEP)) {
+ if (!done_auto_lookup && strequal(name, "*")) {
status = internal_resolve_name(domain, 0x1C, sitename,
&auto_ip_list,
&auto_count,
/* fill in the return list now with real IP's */
while ((local_count<num_addresses) &&
- next_token(&p,name,LIST_SEP,sizeof(name))) {
+ next_token_talloc(ctx, &p, &name, LIST_SEP)) {
struct sockaddr_storage name_ss;
/* copy any addersses from the auto lookup */
/* explicit lookup; resolve_name() will
* handle names & IP addresses */
- if (resolve_name( name, &name_ss, 0x20 )) {
+ if (resolve_name( name, &name_ss, 0x20, true )) {
char addr[INET6_ADDRSTRLEN];
print_sockaddr(addr,
sizeof(addr),
local_count );
}
+ /* For DC's we always prioritize IPv4 due to W2K3 not
+ * supporting LDAP, KRB5 or CLDAP over IPv6. */
+
+ if (local_count && return_iplist) {
+ prioritize_ipv4_list(return_iplist, local_count);
+ }
+
if ( DEBUGLEVEL >= 4 ) {
DEBUG(4,("get_dc_list: returning %d ip addresses "
"in an %sordered list\n",
out:
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(return_iplist);
+ *ip_list = NULL;
+ *count = 0;
+ }
+
SAFE_FREE(auto_ip_list);
TALLOC_FREE(ctx);
return status;
int *count,
bool ads_only )
{
- bool ordered;
+ bool ordered = false;
NTSTATUS status;
enum dc_lookup_type lookup_type = DC_NORMAL_LOOKUP;
+ *ip_list = NULL;
+ *count = 0;
+
DEBUG(8,("get_sorted_dc_list: attempting lookup "
"for name %s (sitename %s) using [%s]\n",
domain,
status = get_dc_list(domain, sitename, ip_list,
count, lookup_type, &ordered);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS)
+ && sitename) {
+ DEBUG(3,("get_sorted_dc_list: no server for name %s available"
+ " in site %s, fallback to all servers\n",
+ domain, sitename));
+ status = get_dc_list(domain, NULL, ip_list,
+ count, lookup_type, &ordered);
+ }
+
if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(*ip_list);
+ *count = 0;
return status;
}
count, DC_KDC_ONLY, &ordered);
if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(*ip_list);
+ *count = 0;
return status;
}