2008-01-10 20:10:23 +03:00
/*
* fs / cifs / dns_resolve . c
*
* Copyright ( c ) 2007 Igor Mammedov
* Author ( s ) : Igor Mammedov ( niallain @ gmail . com )
* Steve French ( sfrench @ us . ibm . com )
*
* Contains the CIFS DFS upcall routines used for hostname to
* IP address translation .
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <keys/user-type.h>
# include "dns_resolve.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_debug.h"
static int dns_resolver_instantiate ( struct key * key , const void * data ,
size_t datalen )
{
int rc = 0 ;
char * ip ;
ip = kmalloc ( datalen + 1 , GFP_KERNEL ) ;
if ( ! ip )
return - ENOMEM ;
memcpy ( ip , data , datalen ) ;
ip [ datalen ] = ' \0 ' ;
rcu_assign_pointer ( key - > payload . data , ip ) ;
return rc ;
}
struct key_type key_type_dns_resolver = {
. name = " dns_resolver " ,
. def_datalen = sizeof ( struct in_addr ) ,
. describe = user_describe ,
. instantiate = dns_resolver_instantiate ,
. match = user_match ,
} ;
2008-04-26 04:22:23 +04:00
/* Checks if supplied name is IP address
* returns :
* 1 - name is IP
* 0 - name is not IP
*/
static int is_ip ( const char * name )
{
int rc ;
struct sockaddr_in sin_server ;
struct sockaddr_in6 sin_server6 ;
rc = cifs_inet_pton ( AF_INET , name ,
& sin_server . sin_addr . s_addr ) ;
if ( rc < = 0 ) {
/* not ipv4 address, try ipv6 */
rc = cifs_inet_pton ( AF_INET6 , name ,
& sin_server6 . sin6_addr . in6_u ) ;
if ( rc > 0 )
return 1 ;
} else {
return 1 ;
}
/* we failed translating address */
return 0 ;
}
2008-01-10 20:10:23 +03:00
/* Resolves server name to ip address.
* input :
* unc - server UNC
* output :
* * ip_addr - pointer to server ip , caller responcible for freeing it .
* return 0 on success
*/
int
2008-01-25 13:12:41 +03:00
dns_resolve_server_name_to_ip ( const char * unc , char * * ip_addr )
{
2008-01-10 20:10:23 +03:00
int rc = - EAGAIN ;
2008-04-26 04:22:23 +04:00
struct key * rkey = ERR_PTR ( - EAGAIN ) ;
2008-01-10 20:10:23 +03:00
char * name ;
2008-04-26 04:22:23 +04:00
char * data = NULL ;
2008-01-10 20:10:23 +03:00
int len ;
2008-01-25 13:12:41 +03:00
if ( ! ip_addr | | ! unc )
2008-01-10 20:10:23 +03:00
return - EINVAL ;
/* search for server name delimiter */
len = strlen ( unc ) ;
if ( len < 3 ) {
2008-03-10 20:14:34 +03:00
cFYI ( 1 , ( " %s: unc is too short: %s " , __func__ , unc ) ) ;
2008-01-10 20:10:23 +03:00
return - EINVAL ;
}
len - = 2 ;
name = memchr ( unc + 2 , ' \\ ' , len ) ;
if ( ! name ) {
cFYI ( 1 , ( " %s: probably server name is whole unc: %s " ,
2008-03-10 20:14:34 +03:00
__func__ , unc ) ) ;
2008-01-10 20:10:23 +03:00
} else {
len = ( name - unc ) - 2 /* leading // */ ;
}
name = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! name ) {
rc = - ENOMEM ;
return rc ;
}
memcpy ( name , unc + 2 , len ) ;
name [ len ] = 0 ;
2008-04-26 04:22:23 +04:00
if ( is_ip ( name ) ) {
cFYI ( 1 , ( " %s: it is IP, skipping dns upcall: %s " ,
__func__ , name ) ) ;
data = name ;
goto skip_upcall ;
}
2008-01-10 20:10:23 +03:00
rkey = request_key ( & key_type_dns_resolver , name , " " ) ;
if ( ! IS_ERR ( rkey ) ) {
2008-04-26 04:22:23 +04:00
data = rkey - > payload . data ;
cFYI ( 1 , ( " %s: resolved: %s to %s " , __func__ ,
2008-01-10 20:10:23 +03:00
rkey - > description ,
* ip_addr
) ) ;
2008-04-26 04:22:23 +04:00
} else {
cERROR ( 1 , ( " %s: unable to resolve: %s " , __func__ , name ) ) ;
goto out ;
}
skip_upcall :
if ( data ) {
len = strlen ( data ) ;
* ip_addr = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( * ip_addr ) {
memcpy ( * ip_addr , data , len ) ;
( * ip_addr ) [ len ] = ' \0 ' ;
2008-01-10 20:10:23 +03:00
rc = 0 ;
} else {
rc = - ENOMEM ;
}
2008-04-26 04:22:23 +04:00
if ( ! IS_ERR ( rkey ) )
key_put ( rkey ) ;
2008-01-10 20:10:23 +03:00
}
2008-04-26 04:22:23 +04:00
out :
2008-01-10 20:10:23 +03:00
kfree ( name ) ;
return rc ;
}