728d9fa34ca7482d6c46392e036a35000f975567
[libnss-gw-name.git] / libnss_gw_name.c
1 /*  
2  *  Copyright © 2010 Joachim Breitner
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  *  This file incorporates some code from the nss-mdns module,
19  *  © 2004 Lennart Poettering.
20  */
21
22
23 #include <netlink/route/rtnl.h>
24 #include <netlink/route/route.h>
25 #include <nss.h>
26 #include <netdb.h>
27 #include <errno.h>
28 #include <sys/socket.h>
29
30 typedef struct {
31     uint32_t address;
32 } ipv4_address_t;
33
34
35 #define ALIGN(idx) do { \
36   if (idx % sizeof(void*)) \
37     idx += (sizeof(void*) - idx % sizeof(void*)); /* Align on 32 bit boundary */ \
38 } while(0)
39
40
41 static struct nl_addr *
42 find_gateway() {
43         struct nl_cache* route_cache;
44         struct nl_handle *sock;
45         struct nl_object *obj;
46         struct nl_addr *gw = NULL;
47         int err;
48
49         // Allocate a new netlink socket
50         sock = nl_handle_alloc();
51
52         err = nl_connect(sock, NETLINK_ROUTE);
53         if (err) {
54                 nl_handle_destroy(sock);
55                 return NULL;
56         }
57
58         route_cache = rtnl_route_alloc_cache (sock);
59         if (!route_cache) {
60                 nl_close(sock);
61                 nl_handle_destroy(sock);
62                 return NULL;
63         }
64
65         for (obj = nl_cache_get_first(route_cache); obj; obj = nl_cache_get_next(obj)) {
66                 struct rtnl_route *route = (struct rtnl_route *)obj;
67
68                 // Ignore non ipv4 routes
69                 if (rtnl_route_get_family(route) != AF_INET) continue;
70
71                 // Find a default route
72                 if (rtnl_route_get_dst_len(route) != 0) continue;
73                 
74                 // Found a gateway
75                 gw = nl_addr_clone(rtnl_route_get_gateway (route));
76
77                 //char buf[100];
78                 //printf("Addr: %s\n", nl_addr2str(dst,buf,100));
79         }
80         
81         // Free the cache
82         nl_cache_free(route_cache);
83
84         // Close the socket first to release kernel memory
85         nl_close(sock);
86
87         // Finally destroy the netlink handle
88         nl_handle_destroy(sock);
89
90         return gw;
91 }
92
93 enum nss_status _nss_gw_name_gethostbyname_r (
94         const char *name,
95         struct hostent *result,
96         char *buffer,
97         size_t buflen,
98         int *errnop,
99         int *h_errnop) {
100
101         size_t idx, astart;
102         struct nl_addr *gw;
103
104         if (!strcmp(name,"gateway.current")) {
105                 // Look up gatway
106                 gw = find_gateway();
107                 if (!gw) {
108                         *errnop = EAGAIN;
109                         *h_errnop = NO_RECOVERY;
110                         return NSS_STATUS_TRYAGAIN;
111                 }
112
113                 if (buflen <
114                         sizeof(char*)+    /* alias names */
115                         strlen(name)+1+   /* main name */
116                         sizeof(ipv4_address_t)+ /* address */
117                         sizeof(char*)+ /* null address pointer */
118                         8 /* Alignment */
119                         )  {   /* official name */
120
121                         nl_addr_destroy(gw);
122
123                         *errnop = ERANGE;
124                         *h_errnop = NO_RECOVERY;
125                         return NSS_STATUS_TRYAGAIN;
126                 }
127         
128
129                 /* Alias names */
130                 *((char**) buffer) = NULL;
131                 result->h_aliases = (char**) buffer;
132                 idx = sizeof(char*);
133
134                 /* Official name */
135                 strcpy(buffer+idx, name); 
136                 result->h_name = buffer+idx;
137                 idx += strlen(name)+1;
138                 ALIGN(idx);
139
140                 result->h_addrtype = AF_INET;
141                 result->h_length = sizeof(ipv4_address_t);
142
143                 astart = idx;
144                 memcpy(buffer+astart, nl_addr_get_binary_addr(gw), sizeof(ipv4_address_t));
145                 idx += sizeof(ipv4_address_t);
146                 
147                 result->h_addr_list = (char**)(buffer + idx);
148                 result->h_addr_list[0] = buffer + astart;
149                 result->h_addr_list[1] = NULL;
150
151                 nl_addr_destroy(gw);
152                 return NSS_STATUS_SUCCESS;
153         }{
154                 *errnop = EINVAL;
155                 *h_errnop = NO_RECOVERY;
156                 return NSS_STATUS_UNAVAIL;
157         }
158 }
159
160 enum nss_status _nss_gw_name_gethostbyname2_r(
161         const char *name,
162         int af,
163         struct hostent * result,
164         char *buffer,
165         size_t buflen,
166         int *errnop,
167         int *h_errnop) {
168
169         if (af != AF_INET) {
170                 *errnop = EAGAIN;
171                 *h_errnop = NO_RECOVERY;
172                 return NSS_STATUS_TRYAGAIN;
173         } else {
174                 return _nss_gw_name_gethostbyname_r(name, result, buffer, buflen, errnop, h_errnop);
175         }
176 }
177