/* Upcall routine, designed to work as a key type and working through1* /sbin/request-key to contact userspace when handling DNS queries.2*3* See Documentation/networking/dns_resolver.txt4*5* Copyright (c) 2007 Igor Mammedov6* Author(s): Igor Mammedov ([email protected])7* Steve French ([email protected])8* Wang Lei ([email protected])9* David Howells ([email protected])10*11* The upcall wrapper used to make an arbitrary DNS query.12*13* This function requires the appropriate userspace tool dns.upcall to be14* installed and something like the following lines should be added to the15* /etc/request-key.conf file:16*17* create dns_resolver * * /sbin/dns.upcall %k18*19* For example to use this module to query AFSDB RR:20*21* create dns_resolver afsdb:* * /sbin/dns.afsdb %k22*23* This library is free software; you can redistribute it and/or modify24* it under the terms of the GNU Lesser General Public License as published25* by the Free Software Foundation; either version 2.1 of the License, or26* (at your option) any later version.27*28* This library is distributed in the hope that it will be useful,29* but WITHOUT ANY WARRANTY; without even the implied warranty of30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See31* the GNU Lesser General Public License for more details.32*33* You should have received a copy of the GNU Lesser General Public License34* along with this library; if not, write to the Free Software35* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA36*/3738#include <linux/module.h>39#include <linux/slab.h>40#include <linux/dns_resolver.h>41#include <linux/err.h>42#include <keys/dns_resolver-type.h>43#include <keys/user-type.h>4445#include "internal.h"4647/**48* dns_query - Query the DNS49* @type: Query type (or NULL for straight host->IP lookup)50* @name: Name to look up51* @namelen: Length of name52* @options: Request options (or NULL if no options)53* @_result: Where to place the returned data.54* @_expiry: Where to store the result expiry time (or NULL)55*56* The data will be returned in the pointer at *result, and the caller is57* responsible for freeing it.58*59* The description should be of the form "[<query_type>:]<domain_name>", and60* the options need to be appropriate for the query type requested. If no61* query_type is given, then the query is a straight hostname to IP address62* lookup.63*64* The DNS resolution lookup is performed by upcalling to userspace by way of65* requesting a key of type dns_resolver.66*67* Returns the size of the result on success, -ve error code otherwise.68*/69int dns_query(const char *type, const char *name, size_t namelen,70const char *options, char **_result, time_t *_expiry)71{72struct key *rkey;73struct user_key_payload *upayload;74const struct cred *saved_cred;75size_t typelen, desclen;76char *desc, *cp;77int ret, len;7879kenter("%s,%*.*s,%zu,%s",80type, (int)namelen, (int)namelen, name, namelen, options);8182if (!name || namelen == 0 || !_result)83return -EINVAL;8485/* construct the query key description as "[<type>:]<name>" */86typelen = 0;87desclen = 0;88if (type) {89typelen = strlen(type);90if (typelen < 1)91return -EINVAL;92desclen += typelen + 1;93}9495if (!namelen)96namelen = strlen(name);97if (namelen < 3)98return -EINVAL;99desclen += namelen + 1;100101desc = kmalloc(desclen, GFP_KERNEL);102if (!desc)103return -ENOMEM;104105cp = desc;106if (type) {107memcpy(cp, type, typelen);108cp += typelen;109*cp++ = ':';110}111memcpy(cp, name, namelen);112cp += namelen;113*cp = '\0';114115if (!options)116options = "";117kdebug("call request_key(,%s,%s)", desc, options);118119/* make the upcall, using special credentials to prevent the use of120* add_key() to preinstall malicious redirections121*/122saved_cred = override_creds(dns_resolver_cache);123rkey = request_key(&key_type_dns_resolver, desc, options);124revert_creds(saved_cred);125kfree(desc);126if (IS_ERR(rkey)) {127ret = PTR_ERR(rkey);128goto out;129}130131down_read(&rkey->sem);132rkey->perm |= KEY_USR_VIEW;133134ret = key_validate(rkey);135if (ret < 0)136goto put;137138/* If the DNS server gave an error, return that to the caller */139ret = rkey->type_data.x[0];140if (ret)141goto put;142143upayload = rcu_dereference_protected(rkey->payload.data,144lockdep_is_held(&rkey->sem));145len = upayload->datalen;146147ret = -ENOMEM;148*_result = kmalloc(len + 1, GFP_KERNEL);149if (!*_result)150goto put;151152memcpy(*_result, upayload->data, len + 1);153if (_expiry)154*_expiry = rkey->expiry;155156ret = len;157put:158up_read(&rkey->sem);159key_put(rkey);160out:161kleave(" = %d", ret);162return ret;163}164EXPORT_SYMBOL(dns_query);165166167