f999cd990ca04506f6cda6e198795dc216d395c3
[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                 struct nl_addr *gw_ = rtnl_route_get_gateway(route);
76                 if (!gw_) continue;
77
78                 // Clone the address, as this one will be freed with the route cache (will it?)
79                 gw = nl_addr_clone(gw_);
80                 if (!gw) continue;
81
82                 break;
83
84                 //char buf[100];
85                 //printf("Addr: %s\n", nl_addr2str(dst,buf,100));
86         }
87         
88         // Free the cache
89         nl_cache_free(route_cache);
90
91         // Close the socket first to release kernel memory
92         nl_close(sock);
93
94         // Finally destroy the netlink handle
95         nl_handle_destroy(sock);
96
97         return gw;
98 }
99
100 enum nss_status _nss_gw_name_gethostbyname_r (
101         const char *name,
102         struct hostent *result,
103         char *buffer,
104         size_t buflen,
105         int *errnop,
106         int *h_errnop) {
107
108         size_t idx, astart;
109         struct nl_addr *gw;
110
111         if (!strcmp(name,"gateway.current")) {
112                 // Look up gatway
113                 gw = find_gateway();
114                 if (!gw) {
115                         *errnop = EAGAIN;
116                         *h_errnop = NO_RECOVERY;
117                         return NSS_STATUS_TRYAGAIN;
118                 }
119
120                 if (buflen <
121                         sizeof(char*)+    /* alias names */
122                         strlen(name)+1+   /* main name */
123                         sizeof(ipv4_address_t)+ /* address */
124                         sizeof(char*)+ /* null address pointer */
125                         8 /* Alignment */
126                         )  {   /* official name */
127
128                         nl_addr_destroy(gw);
129
130                         *errnop = ERANGE;
131                         *h_errnop = NO_RECOVERY;
132                         return NSS_STATUS_TRYAGAIN;
133                 }
134         
135
136                 /* Alias names */
137                 *((char**) buffer) = NULL;
138                 result->h_aliases = (char**) buffer;
139                 idx = sizeof(char*);
140
141                 /* Official name */
142                 strcpy(buffer+idx, name); 
143                 result->h_name = buffer+idx;
144                 idx += strlen(name)+1;
145                 ALIGN(idx);
146
147                 result->h_addrtype = AF_INET;
148                 result->h_length = sizeof(ipv4_address_t);
149
150                 astart = idx;
151                 memcpy(buffer+astart, nl_addr_get_binary_addr(gw), sizeof(ipv4_address_t));
152                 idx += sizeof(ipv4_address_t);
153                 
154                 result->h_addr_list = (char**)(buffer + idx);
155                 result->h_addr_list[0] = buffer + astart;
156                 result->h_addr_list[1] = NULL;
157
158                 nl_addr_destroy(gw);
159                 return NSS_STATUS_SUCCESS;
160         }{
161                 *errnop = EINVAL;
162                 *h_errnop = NO_RECOVERY;
163                 return NSS_STATUS_UNAVAIL;
164         }
165 }
166
167 enum nss_status _nss_gw_name_gethostbyname2_r(
168         const char *name,
169         int af,
170         struct hostent * result,
171         char *buffer,
172         size_t buflen,
173         int *errnop,
174         int *h_errnop) {
175
176         if (af != AF_INET) {
177                 *errnop = EAGAIN;
178                 *h_errnop = NO_RECOVERY;
179                 return NSS_STATUS_TRYAGAIN;
180         } else {
181                 return _nss_gw_name_gethostbyname_r(name, result, buffer, buflen, errnop, h_errnop);
182         }
183 }
184