releasing version 0.3-2
[libnss-gw-name.git] / libnss_gw_name.c
1 /*  
2  *  Copyright © 2010,2012 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 #include <netlink/socket.h>
23 #include <netlink/route/route.h>
24 #include <nss.h>
25 #include <netdb.h>
26 #include <errno.h>
27 #include <sys/socket.h>
28
29 typedef struct {
30     uint32_t address;
31 } ipv4_address_t;
32
33
34 #define ALIGN(idx) do { \
35   if (idx % sizeof(void*)) \
36     idx += (sizeof(void*) - idx % sizeof(void*)); /* Align on 32 bit boundary */ \
37 } while(0)
38
39
40 static struct nl_addr *
41 find_gateway() {
42         struct nl_cache* route_cache;
43         struct nl_sock *sock;
44         struct nl_object *obj;
45         struct nl_addr *gw = NULL;
46         int err;
47
48         // Allocate a new netlink socket
49         sock = nl_socket_alloc();
50
51         err = nl_connect(sock, NETLINK_ROUTE);
52         if (err) {
53                 nl_socket_free(sock);
54                 return NULL;
55         }
56
57         if (rtnl_route_alloc_cache(sock, AF_INET, 0, &route_cache)) {
58                 nl_close(sock);
59                 nl_socket_free(sock);
60                 return NULL;
61         }
62
63         for (obj = nl_cache_get_first(route_cache); obj; obj = nl_cache_get_next(obj)) {
64                 struct rtnl_route *route = (struct rtnl_route *)obj;
65
66                 // Ignore non ipv4 routes
67                 if (rtnl_route_get_family(route) != AF_INET) continue;
68
69                 // Find a default route
70                 if (nl_addr_get_prefixlen(rtnl_route_get_dst(route)) != 0) continue;
71
72                 // Assert a next hop
73                 if (rtnl_route_get_nnexthops(route) < 1) continue;
74                 
75                 // Found a gateway
76                 struct nl_addr *gw_ = rtnl_route_nh_get_gateway(rtnl_route_nexthop_n(route, 0));
77                 if (!gw_) continue;
78
79                 // Clone the address, as this one will be freed with the route cache (will it?)
80                 gw = nl_addr_clone(gw_);
81                 if (!gw) continue;
82
83                 break;
84
85                 //char buf[100];
86                 //printf("Addr: %s\n", nl_addr2str(dst,buf,100));
87         }
88         
89         // Free the cache
90         nl_cache_free(route_cache);
91
92         // Close the socket first to release kernel memory
93         nl_close(sock);
94
95         // Finally destroy the netlink handle
96         nl_socket_free(sock);
97
98         return gw;
99 }
100
101 enum nss_status _nss_gw_name_gethostbyname_r (
102         const char *name,
103         struct hostent *result,
104         char *buffer,
105         size_t buflen,
106         int *errnop,
107         int *h_errnop) {
108
109         size_t idx, astart;
110         struct nl_addr *gw;
111
112         if (!strcmp(name,"gateway.localhost")) {
113                 // Look up gatway
114                 gw = find_gateway();
115                 if (!gw) {
116                         *errnop = EAGAIN;
117                         *h_errnop = NO_RECOVERY;
118                         return NSS_STATUS_TRYAGAIN;
119                 }
120
121                 if (buflen <
122                         sizeof(char*)+    /* alias names */
123                         strlen(name)+1+   /* main name */
124                         sizeof(ipv4_address_t)+ /* address */
125                         sizeof(char*)+ /* null address pointer */
126                         8 /* Alignment */
127                         )  {   /* official name */
128
129                         nl_addr_put(gw);
130
131                         *errnop = ERANGE;
132                         *h_errnop = NO_RECOVERY;
133                         return NSS_STATUS_TRYAGAIN;
134                 }
135         
136
137                 /* Alias names */
138                 *((char**) buffer) = NULL;
139                 result->h_aliases = (char**) buffer;
140                 idx = sizeof(char*);
141
142                 /* Official name */
143                 strcpy(buffer+idx, name); 
144                 result->h_name = buffer+idx;
145                 idx += strlen(name)+1;
146                 ALIGN(idx);
147
148                 result->h_addrtype = AF_INET;
149                 result->h_length = sizeof(ipv4_address_t);
150
151                 astart = idx;
152                 memcpy(buffer+astart, nl_addr_get_binary_addr(gw), sizeof(ipv4_address_t));
153                 idx += sizeof(ipv4_address_t);
154                 
155                 result->h_addr_list = (char**)(buffer + idx);
156                 result->h_addr_list[0] = buffer + astart;
157                 result->h_addr_list[1] = NULL;
158
159                 nl_addr_put(gw);
160                 return NSS_STATUS_SUCCESS;
161         }{
162                 *errnop = EINVAL;
163                 *h_errnop = NO_RECOVERY;
164                 return NSS_STATUS_UNAVAIL;
165         }
166 }
167
168 enum nss_status _nss_gw_name_gethostbyname2_r(
169         const char *name,
170         int af,
171         struct hostent * result,
172         char *buffer,
173         size_t buflen,
174         int *errnop,
175         int *h_errnop) {
176
177         if (af != AF_INET) {
178                 *errnop = EAGAIN;
179                 *h_errnop = NO_RECOVERY;
180                 return NSS_STATUS_TRYAGAIN;
181         } else {
182                 return _nss_gw_name_gethostbyname_r(name, result, buffer, buflen, errnop, h_errnop);
183         }
184 }
185