xref: /aosp_15_r20/external/dnsmasq/src/lease.c (revision c2c26c8b25cb2c9c4fe49a734c2305a522f5635e)
1*c2c26c8bSAndroid Build Coastguard Worker /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
2*c2c26c8bSAndroid Build Coastguard Worker 
3*c2c26c8bSAndroid Build Coastguard Worker    This program is free software; you can redistribute it and/or modify
4*c2c26c8bSAndroid Build Coastguard Worker    it under the terms of the GNU General Public License as published by
5*c2c26c8bSAndroid Build Coastguard Worker    the Free Software Foundation; version 2 dated June, 1991, or
6*c2c26c8bSAndroid Build Coastguard Worker    (at your option) version 3 dated 29 June, 2007.
7*c2c26c8bSAndroid Build Coastguard Worker 
8*c2c26c8bSAndroid Build Coastguard Worker    This program is distributed in the hope that it will be useful,
9*c2c26c8bSAndroid Build Coastguard Worker    but WITHOUT ANY WARRANTY; without even the implied warranty of
10*c2c26c8bSAndroid Build Coastguard Worker    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11*c2c26c8bSAndroid Build Coastguard Worker    GNU General Public License for more details.
12*c2c26c8bSAndroid Build Coastguard Worker 
13*c2c26c8bSAndroid Build Coastguard Worker    You should have received a copy of the GNU General Public License
14*c2c26c8bSAndroid Build Coastguard Worker    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*c2c26c8bSAndroid Build Coastguard Worker */
16*c2c26c8bSAndroid Build Coastguard Worker 
17*c2c26c8bSAndroid Build Coastguard Worker #include "dnsmasq.h"
18*c2c26c8bSAndroid Build Coastguard Worker 
19*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_DHCP
20*c2c26c8bSAndroid Build Coastguard Worker 
21*c2c26c8bSAndroid Build Coastguard Worker static struct dhcp_lease *leases = NULL, *old_leases = NULL;
22*c2c26c8bSAndroid Build Coastguard Worker static int dns_dirty, file_dirty, leases_left;
23*c2c26c8bSAndroid Build Coastguard Worker 
lease_init(time_t now)24*c2c26c8bSAndroid Build Coastguard Worker void lease_init(time_t now) {
25*c2c26c8bSAndroid Build Coastguard Worker     unsigned long ei;
26*c2c26c8bSAndroid Build Coastguard Worker     struct in_addr addr;
27*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
28*c2c26c8bSAndroid Build Coastguard Worker     int clid_len, hw_len, hw_type;
29*c2c26c8bSAndroid Build Coastguard Worker     FILE* leasestream;
30*c2c26c8bSAndroid Build Coastguard Worker 
31*c2c26c8bSAndroid Build Coastguard Worker     /* These two each hold a DHCP option max size 255
32*c2c26c8bSAndroid Build Coastguard Worker        and get a terminating zero added */
33*c2c26c8bSAndroid Build Coastguard Worker     daemon->dhcp_buff = safe_malloc(256);
34*c2c26c8bSAndroid Build Coastguard Worker     daemon->dhcp_buff2 = safe_malloc(256);
35*c2c26c8bSAndroid Build Coastguard Worker 
36*c2c26c8bSAndroid Build Coastguard Worker     leases_left = daemon->dhcp_max;
37*c2c26c8bSAndroid Build Coastguard Worker 
38*c2c26c8bSAndroid Build Coastguard Worker     if (daemon->options & OPT_LEASE_RO) {
39*c2c26c8bSAndroid Build Coastguard Worker         /* run "<lease_change_script> init" once to get the
40*c2c26c8bSAndroid Build Coastguard Worker        initial state of the database. If leasefile-ro is
41*c2c26c8bSAndroid Build Coastguard Worker        set without a script, we just do without any
42*c2c26c8bSAndroid Build Coastguard Worker        lease database. */
43*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_SCRIPT
44*c2c26c8bSAndroid Build Coastguard Worker         if (daemon->lease_change_command) {
45*c2c26c8bSAndroid Build Coastguard Worker             strcpy(daemon->dhcp_buff, daemon->lease_change_command);
46*c2c26c8bSAndroid Build Coastguard Worker             strcat(daemon->dhcp_buff, " init");
47*c2c26c8bSAndroid Build Coastguard Worker             leasestream = popen(daemon->dhcp_buff, "r");
48*c2c26c8bSAndroid Build Coastguard Worker         } else
49*c2c26c8bSAndroid Build Coastguard Worker #endif
50*c2c26c8bSAndroid Build Coastguard Worker         {
51*c2c26c8bSAndroid Build Coastguard Worker             file_dirty = dns_dirty = 0;
52*c2c26c8bSAndroid Build Coastguard Worker             return;
53*c2c26c8bSAndroid Build Coastguard Worker         }
54*c2c26c8bSAndroid Build Coastguard Worker 
55*c2c26c8bSAndroid Build Coastguard Worker     } else {
56*c2c26c8bSAndroid Build Coastguard Worker         /* NOTE: need a+ mode to create file if it doesn't exist */
57*c2c26c8bSAndroid Build Coastguard Worker         leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
58*c2c26c8bSAndroid Build Coastguard Worker 
59*c2c26c8bSAndroid Build Coastguard Worker         if (!leasestream)
60*c2c26c8bSAndroid Build Coastguard Worker             die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
61*c2c26c8bSAndroid Build Coastguard Worker 
62*c2c26c8bSAndroid Build Coastguard Worker         /* a+ mode leaves pointer at end. */
63*c2c26c8bSAndroid Build Coastguard Worker         rewind(leasestream);
64*c2c26c8bSAndroid Build Coastguard Worker     }
65*c2c26c8bSAndroid Build Coastguard Worker 
66*c2c26c8bSAndroid Build Coastguard Worker     /* client-id max length is 255 which is 255*2 digits + 254 colons
67*c2c26c8bSAndroid Build Coastguard Worker        borrow DNS packet buffer which is always larger than 1000 bytes */
68*c2c26c8bSAndroid Build Coastguard Worker     if (leasestream)
69*c2c26c8bSAndroid Build Coastguard Worker         while (fscanf(leasestream, "%lu %255s %16s %255s %764s", &ei, daemon->dhcp_buff2,
70*c2c26c8bSAndroid Build Coastguard Worker                       daemon->namebuff, daemon->dhcp_buff, daemon->packet) == 5) {
71*c2c26c8bSAndroid Build Coastguard Worker             hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char*) daemon->dhcp_buff2,
72*c2c26c8bSAndroid Build Coastguard Worker                                DHCP_CHADDR_MAX, NULL, &hw_type);
73*c2c26c8bSAndroid Build Coastguard Worker             /* For backwards compatibility, no explict MAC address type means ether. */
74*c2c26c8bSAndroid Build Coastguard Worker             if (hw_type == 0 && hw_len != 0) hw_type = ARPHRD_ETHER;
75*c2c26c8bSAndroid Build Coastguard Worker 
76*c2c26c8bSAndroid Build Coastguard Worker             addr.s_addr = inet_addr(daemon->namebuff);
77*c2c26c8bSAndroid Build Coastguard Worker 
78*c2c26c8bSAndroid Build Coastguard Worker             /* decode hex in place */
79*c2c26c8bSAndroid Build Coastguard Worker             clid_len = 0;
80*c2c26c8bSAndroid Build Coastguard Worker             if (strcmp(daemon->packet, "*") != 0)
81*c2c26c8bSAndroid Build Coastguard Worker                 clid_len =
82*c2c26c8bSAndroid Build Coastguard Worker                     parse_hex(daemon->packet, (unsigned char*) daemon->packet, 255, NULL, NULL);
83*c2c26c8bSAndroid Build Coastguard Worker 
84*c2c26c8bSAndroid Build Coastguard Worker             if (!(lease = lease_allocate(addr))) die(_("too many stored leases"), NULL, EC_MISC);
85*c2c26c8bSAndroid Build Coastguard Worker 
86*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_BROKEN_RTC
87*c2c26c8bSAndroid Build Coastguard Worker             if (ei != 0)
88*c2c26c8bSAndroid Build Coastguard Worker                 lease->expires = (time_t) ei + now;
89*c2c26c8bSAndroid Build Coastguard Worker             else
90*c2c26c8bSAndroid Build Coastguard Worker                 lease->expires = (time_t) 0;
91*c2c26c8bSAndroid Build Coastguard Worker             lease->length = ei;
92*c2c26c8bSAndroid Build Coastguard Worker #else
93*c2c26c8bSAndroid Build Coastguard Worker             /* strictly time_t is opaque, but this hack should work on all sane systems,
94*c2c26c8bSAndroid Build Coastguard Worker                even when sizeof(time_t) == 8 */
95*c2c26c8bSAndroid Build Coastguard Worker             lease->expires = (time_t) ei;
96*c2c26c8bSAndroid Build Coastguard Worker #endif
97*c2c26c8bSAndroid Build Coastguard Worker 
98*c2c26c8bSAndroid Build Coastguard Worker             lease_set_hwaddr(lease, (unsigned char*) daemon->dhcp_buff2,
99*c2c26c8bSAndroid Build Coastguard Worker                              (unsigned char*) daemon->packet, hw_len, hw_type, clid_len);
100*c2c26c8bSAndroid Build Coastguard Worker 
101*c2c26c8bSAndroid Build Coastguard Worker             if (strcmp(daemon->dhcp_buff, "*") != 0)
102*c2c26c8bSAndroid Build Coastguard Worker                 lease_set_hostname(lease, daemon->dhcp_buff, 0);
103*c2c26c8bSAndroid Build Coastguard Worker 
104*c2c26c8bSAndroid Build Coastguard Worker             /* set these correctly: the "old" events are generated later from
105*c2c26c8bSAndroid Build Coastguard Worker                the startup synthesised SIGHUP. */
106*c2c26c8bSAndroid Build Coastguard Worker             lease->new = lease->changed = 0;
107*c2c26c8bSAndroid Build Coastguard Worker         }
108*c2c26c8bSAndroid Build Coastguard Worker 
109*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_SCRIPT
110*c2c26c8bSAndroid Build Coastguard Worker     if (!daemon->lease_stream) {
111*c2c26c8bSAndroid Build Coastguard Worker         int rc = 0;
112*c2c26c8bSAndroid Build Coastguard Worker 
113*c2c26c8bSAndroid Build Coastguard Worker         /* shell returns 127 for "command not found", 126 for bad permissions. */
114*c2c26c8bSAndroid Build Coastguard Worker         if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 ||
115*c2c26c8bSAndroid Build Coastguard Worker             WEXITSTATUS(rc) == 126) {
116*c2c26c8bSAndroid Build Coastguard Worker             if (WEXITSTATUS(rc) == 127)
117*c2c26c8bSAndroid Build Coastguard Worker                 errno = ENOENT;
118*c2c26c8bSAndroid Build Coastguard Worker             else if (WEXITSTATUS(rc) == 126)
119*c2c26c8bSAndroid Build Coastguard Worker                 errno = EACCES;
120*c2c26c8bSAndroid Build Coastguard Worker             die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
121*c2c26c8bSAndroid Build Coastguard Worker         }
122*c2c26c8bSAndroid Build Coastguard Worker 
123*c2c26c8bSAndroid Build Coastguard Worker         if (WEXITSTATUS(rc) != 0) {
124*c2c26c8bSAndroid Build Coastguard Worker             sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
125*c2c26c8bSAndroid Build Coastguard Worker             die(_("lease-init script returned exit code %s"), daemon->dhcp_buff,
126*c2c26c8bSAndroid Build Coastguard Worker                 WEXITSTATUS(rc) + EC_INIT_OFFSET);
127*c2c26c8bSAndroid Build Coastguard Worker         }
128*c2c26c8bSAndroid Build Coastguard Worker     }
129*c2c26c8bSAndroid Build Coastguard Worker #endif
130*c2c26c8bSAndroid Build Coastguard Worker 
131*c2c26c8bSAndroid Build Coastguard Worker     /* Some leases may have expired */
132*c2c26c8bSAndroid Build Coastguard Worker     file_dirty = 0;
133*c2c26c8bSAndroid Build Coastguard Worker     lease_prune(NULL, now);
134*c2c26c8bSAndroid Build Coastguard Worker     dns_dirty = 1;
135*c2c26c8bSAndroid Build Coastguard Worker }
136*c2c26c8bSAndroid Build Coastguard Worker 
lease_update_from_configs(void)137*c2c26c8bSAndroid Build Coastguard Worker void lease_update_from_configs(void) {
138*c2c26c8bSAndroid Build Coastguard Worker     /* changes to the config may change current leases. */
139*c2c26c8bSAndroid Build Coastguard Worker 
140*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
141*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_config* config;
142*c2c26c8bSAndroid Build Coastguard Worker     char* name;
143*c2c26c8bSAndroid Build Coastguard Worker 
144*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases; lease; lease = lease->next)
145*c2c26c8bSAndroid Build Coastguard Worker         if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len,
146*c2c26c8bSAndroid Build Coastguard Worker                                   lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
147*c2c26c8bSAndroid Build Coastguard Worker             (config->flags & CONFIG_NAME) &&
148*c2c26c8bSAndroid Build Coastguard Worker             (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
149*c2c26c8bSAndroid Build Coastguard Worker             lease_set_hostname(lease, config->hostname, 1);
150*c2c26c8bSAndroid Build Coastguard Worker         else if ((name = host_from_dns(lease->addr)))
151*c2c26c8bSAndroid Build Coastguard Worker             lease_set_hostname(lease, name, 1); /* updates auth flag only */
152*c2c26c8bSAndroid Build Coastguard Worker }
153*c2c26c8bSAndroid Build Coastguard Worker 
ourprintf(int * errp,char * format,...)154*c2c26c8bSAndroid Build Coastguard Worker static void ourprintf(int* errp, char* format, ...) {
155*c2c26c8bSAndroid Build Coastguard Worker     va_list ap;
156*c2c26c8bSAndroid Build Coastguard Worker 
157*c2c26c8bSAndroid Build Coastguard Worker     va_start(ap, format);
158*c2c26c8bSAndroid Build Coastguard Worker     if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0) *errp = errno;
159*c2c26c8bSAndroid Build Coastguard Worker     va_end(ap);
160*c2c26c8bSAndroid Build Coastguard Worker }
161*c2c26c8bSAndroid Build Coastguard Worker 
lease_update_file(time_t now)162*c2c26c8bSAndroid Build Coastguard Worker void lease_update_file(time_t now) {
163*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
164*c2c26c8bSAndroid Build Coastguard Worker     time_t next_event;
165*c2c26c8bSAndroid Build Coastguard Worker     int i, err = 0;
166*c2c26c8bSAndroid Build Coastguard Worker 
167*c2c26c8bSAndroid Build Coastguard Worker     if (file_dirty != 0 && daemon->lease_stream) {
168*c2c26c8bSAndroid Build Coastguard Worker         errno = 0;
169*c2c26c8bSAndroid Build Coastguard Worker         rewind(daemon->lease_stream);
170*c2c26c8bSAndroid Build Coastguard Worker         if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0) err = errno;
171*c2c26c8bSAndroid Build Coastguard Worker 
172*c2c26c8bSAndroid Build Coastguard Worker         for (lease = leases; lease; lease = lease->next) {
173*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_BROKEN_RTC
174*c2c26c8bSAndroid Build Coastguard Worker             ourprintf(&err, "%u ", lease->length);
175*c2c26c8bSAndroid Build Coastguard Worker #else
176*c2c26c8bSAndroid Build Coastguard Worker             ourprintf(&err, "%lu ", (unsigned long) lease->expires);
177*c2c26c8bSAndroid Build Coastguard Worker #endif
178*c2c26c8bSAndroid Build Coastguard Worker             if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
179*c2c26c8bSAndroid Build Coastguard Worker                 ourprintf(&err, "%.2x-", lease->hwaddr_type);
180*c2c26c8bSAndroid Build Coastguard Worker             for (i = 0; i < lease->hwaddr_len; i++) {
181*c2c26c8bSAndroid Build Coastguard Worker                 ourprintf(&err, "%.2x", lease->hwaddr[i]);
182*c2c26c8bSAndroid Build Coastguard Worker                 if (i != lease->hwaddr_len - 1) ourprintf(&err, ":");
183*c2c26c8bSAndroid Build Coastguard Worker             }
184*c2c26c8bSAndroid Build Coastguard Worker 
185*c2c26c8bSAndroid Build Coastguard Worker             ourprintf(&err, " %s ", inet_ntoa(lease->addr));
186*c2c26c8bSAndroid Build Coastguard Worker             ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
187*c2c26c8bSAndroid Build Coastguard Worker 
188*c2c26c8bSAndroid Build Coastguard Worker             if (lease->clid && lease->clid_len != 0) {
189*c2c26c8bSAndroid Build Coastguard Worker                 for (i = 0; i < lease->clid_len - 1; i++) ourprintf(&err, "%.2x:", lease->clid[i]);
190*c2c26c8bSAndroid Build Coastguard Worker                 ourprintf(&err, "%.2x\n", lease->clid[i]);
191*c2c26c8bSAndroid Build Coastguard Worker             } else
192*c2c26c8bSAndroid Build Coastguard Worker                 ourprintf(&err, "*\n");
193*c2c26c8bSAndroid Build Coastguard Worker         }
194*c2c26c8bSAndroid Build Coastguard Worker 
195*c2c26c8bSAndroid Build Coastguard Worker         if (fflush(daemon->lease_stream) != 0 || fsync(fileno(daemon->lease_stream)) < 0)
196*c2c26c8bSAndroid Build Coastguard Worker             err = errno;
197*c2c26c8bSAndroid Build Coastguard Worker 
198*c2c26c8bSAndroid Build Coastguard Worker         if (!err) file_dirty = 0;
199*c2c26c8bSAndroid Build Coastguard Worker     }
200*c2c26c8bSAndroid Build Coastguard Worker 
201*c2c26c8bSAndroid Build Coastguard Worker     /* Set alarm for when the first lease expires + slop. */
202*c2c26c8bSAndroid Build Coastguard Worker     for (next_event = 0, lease = leases; lease; lease = lease->next)
203*c2c26c8bSAndroid Build Coastguard Worker         if (lease->expires != 0 &&
204*c2c26c8bSAndroid Build Coastguard Worker             (next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
205*c2c26c8bSAndroid Build Coastguard Worker             next_event = lease->expires + 10;
206*c2c26c8bSAndroid Build Coastguard Worker 
207*c2c26c8bSAndroid Build Coastguard Worker     if (err) {
208*c2c26c8bSAndroid Build Coastguard Worker         if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
209*c2c26c8bSAndroid Build Coastguard Worker             next_event = LEASE_RETRY + now;
210*c2c26c8bSAndroid Build Coastguard Worker 
211*c2c26c8bSAndroid Build Coastguard Worker         my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"), daemon->lease_file,
212*c2c26c8bSAndroid Build Coastguard Worker                   strerror(err), (unsigned int) difftime(next_event, now));
213*c2c26c8bSAndroid Build Coastguard Worker     }
214*c2c26c8bSAndroid Build Coastguard Worker 
215*c2c26c8bSAndroid Build Coastguard Worker     if (next_event != 0) alarm((unsigned) difftime(next_event, now));
216*c2c26c8bSAndroid Build Coastguard Worker }
217*c2c26c8bSAndroid Build Coastguard Worker 
lease_update_dns(void)218*c2c26c8bSAndroid Build Coastguard Worker void lease_update_dns(void) {
219*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
220*c2c26c8bSAndroid Build Coastguard Worker 
221*c2c26c8bSAndroid Build Coastguard Worker     if (daemon->port != 0 && dns_dirty) {
222*c2c26c8bSAndroid Build Coastguard Worker         cache_unhash_dhcp();
223*c2c26c8bSAndroid Build Coastguard Worker 
224*c2c26c8bSAndroid Build Coastguard Worker         for (lease = leases; lease; lease = lease->next) {
225*c2c26c8bSAndroid Build Coastguard Worker             if (lease->fqdn) cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
226*c2c26c8bSAndroid Build Coastguard Worker 
227*c2c26c8bSAndroid Build Coastguard Worker             if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
228*c2c26c8bSAndroid Build Coastguard Worker                 cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
229*c2c26c8bSAndroid Build Coastguard Worker         }
230*c2c26c8bSAndroid Build Coastguard Worker 
231*c2c26c8bSAndroid Build Coastguard Worker         dns_dirty = 0;
232*c2c26c8bSAndroid Build Coastguard Worker     }
233*c2c26c8bSAndroid Build Coastguard Worker }
234*c2c26c8bSAndroid Build Coastguard Worker 
lease_prune(struct dhcp_lease * target,time_t now)235*c2c26c8bSAndroid Build Coastguard Worker void lease_prune(struct dhcp_lease* target, time_t now) {
236*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease *lease, *tmp, **up;
237*c2c26c8bSAndroid Build Coastguard Worker 
238*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases, up = &leases; lease; lease = tmp) {
239*c2c26c8bSAndroid Build Coastguard Worker         tmp = lease->next;
240*c2c26c8bSAndroid Build Coastguard Worker         if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target) {
241*c2c26c8bSAndroid Build Coastguard Worker             file_dirty = 1;
242*c2c26c8bSAndroid Build Coastguard Worker             if (lease->hostname) dns_dirty = 1;
243*c2c26c8bSAndroid Build Coastguard Worker 
244*c2c26c8bSAndroid Build Coastguard Worker             *up = lease->next; /* unlink */
245*c2c26c8bSAndroid Build Coastguard Worker 
246*c2c26c8bSAndroid Build Coastguard Worker             /* Put on old_leases list 'till we
247*c2c26c8bSAndroid Build Coastguard Worker                can run the script */
248*c2c26c8bSAndroid Build Coastguard Worker             lease->next = old_leases;
249*c2c26c8bSAndroid Build Coastguard Worker             old_leases = lease;
250*c2c26c8bSAndroid Build Coastguard Worker 
251*c2c26c8bSAndroid Build Coastguard Worker             leases_left++;
252*c2c26c8bSAndroid Build Coastguard Worker         } else
253*c2c26c8bSAndroid Build Coastguard Worker             up = &lease->next;
254*c2c26c8bSAndroid Build Coastguard Worker     }
255*c2c26c8bSAndroid Build Coastguard Worker }
256*c2c26c8bSAndroid Build Coastguard Worker 
lease_find_by_client(unsigned char * hwaddr,int hw_len,int hw_type,unsigned char * clid,int clid_len)257*c2c26c8bSAndroid Build Coastguard Worker struct dhcp_lease* lease_find_by_client(unsigned char* hwaddr, int hw_len, int hw_type,
258*c2c26c8bSAndroid Build Coastguard Worker                                         unsigned char* clid, int clid_len) {
259*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
260*c2c26c8bSAndroid Build Coastguard Worker 
261*c2c26c8bSAndroid Build Coastguard Worker     if (clid)
262*c2c26c8bSAndroid Build Coastguard Worker         for (lease = leases; lease; lease = lease->next)
263*c2c26c8bSAndroid Build Coastguard Worker             if (lease->clid && clid_len == lease->clid_len &&
264*c2c26c8bSAndroid Build Coastguard Worker                 memcmp(clid, lease->clid, clid_len) == 0)
265*c2c26c8bSAndroid Build Coastguard Worker                 return lease;
266*c2c26c8bSAndroid Build Coastguard Worker 
267*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases; lease; lease = lease->next)
268*c2c26c8bSAndroid Build Coastguard Worker         if ((!lease->clid || !clid) && hw_len != 0 && lease->hwaddr_len == hw_len &&
269*c2c26c8bSAndroid Build Coastguard Worker             lease->hwaddr_type == hw_type && memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
270*c2c26c8bSAndroid Build Coastguard Worker             return lease;
271*c2c26c8bSAndroid Build Coastguard Worker 
272*c2c26c8bSAndroid Build Coastguard Worker     return NULL;
273*c2c26c8bSAndroid Build Coastguard Worker }
274*c2c26c8bSAndroid Build Coastguard Worker 
lease_find_by_addr(struct in_addr addr)275*c2c26c8bSAndroid Build Coastguard Worker struct dhcp_lease* lease_find_by_addr(struct in_addr addr) {
276*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
277*c2c26c8bSAndroid Build Coastguard Worker 
278*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases; lease; lease = lease->next)
279*c2c26c8bSAndroid Build Coastguard Worker         if (lease->addr.s_addr == addr.s_addr) return lease;
280*c2c26c8bSAndroid Build Coastguard Worker 
281*c2c26c8bSAndroid Build Coastguard Worker     return NULL;
282*c2c26c8bSAndroid Build Coastguard Worker }
283*c2c26c8bSAndroid Build Coastguard Worker 
lease_allocate(struct in_addr addr)284*c2c26c8bSAndroid Build Coastguard Worker struct dhcp_lease* lease_allocate(struct in_addr addr) {
285*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
286*c2c26c8bSAndroid Build Coastguard Worker     if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease)))) return NULL;
287*c2c26c8bSAndroid Build Coastguard Worker 
288*c2c26c8bSAndroid Build Coastguard Worker     memset(lease, 0, sizeof(struct dhcp_lease));
289*c2c26c8bSAndroid Build Coastguard Worker     lease->new = 1;
290*c2c26c8bSAndroid Build Coastguard Worker     lease->addr = addr;
291*c2c26c8bSAndroid Build Coastguard Worker     lease->hwaddr_len = 256; /* illegal value */
292*c2c26c8bSAndroid Build Coastguard Worker     lease->expires = 1;
293*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_BROKEN_RTC
294*c2c26c8bSAndroid Build Coastguard Worker     lease->length = 0xffffffff; /* illegal value */
295*c2c26c8bSAndroid Build Coastguard Worker #endif
296*c2c26c8bSAndroid Build Coastguard Worker     lease->next = leases;
297*c2c26c8bSAndroid Build Coastguard Worker     leases = lease;
298*c2c26c8bSAndroid Build Coastguard Worker 
299*c2c26c8bSAndroid Build Coastguard Worker     file_dirty = 1;
300*c2c26c8bSAndroid Build Coastguard Worker     leases_left--;
301*c2c26c8bSAndroid Build Coastguard Worker 
302*c2c26c8bSAndroid Build Coastguard Worker     return lease;
303*c2c26c8bSAndroid Build Coastguard Worker }
304*c2c26c8bSAndroid Build Coastguard Worker 
lease_set_expires(struct dhcp_lease * lease,unsigned int len,time_t now)305*c2c26c8bSAndroid Build Coastguard Worker void lease_set_expires(struct dhcp_lease* lease, unsigned int len, time_t now) {
306*c2c26c8bSAndroid Build Coastguard Worker     time_t exp = now + (time_t) len;
307*c2c26c8bSAndroid Build Coastguard Worker 
308*c2c26c8bSAndroid Build Coastguard Worker     if (len == 0xffffffff) {
309*c2c26c8bSAndroid Build Coastguard Worker         exp = 0;
310*c2c26c8bSAndroid Build Coastguard Worker         len = 0;
311*c2c26c8bSAndroid Build Coastguard Worker     }
312*c2c26c8bSAndroid Build Coastguard Worker 
313*c2c26c8bSAndroid Build Coastguard Worker     if (exp != lease->expires) {
314*c2c26c8bSAndroid Build Coastguard Worker         dns_dirty = 1;
315*c2c26c8bSAndroid Build Coastguard Worker         lease->expires = exp;
316*c2c26c8bSAndroid Build Coastguard Worker #ifndef HAVE_BROKEN_RTC
317*c2c26c8bSAndroid Build Coastguard Worker         lease->aux_changed = file_dirty = 1;
318*c2c26c8bSAndroid Build Coastguard Worker #endif
319*c2c26c8bSAndroid Build Coastguard Worker     }
320*c2c26c8bSAndroid Build Coastguard Worker 
321*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_BROKEN_RTC
322*c2c26c8bSAndroid Build Coastguard Worker     if (len != lease->length) {
323*c2c26c8bSAndroid Build Coastguard Worker         lease->length = len;
324*c2c26c8bSAndroid Build Coastguard Worker         lease->aux_changed = file_dirty = 1;
325*c2c26c8bSAndroid Build Coastguard Worker     }
326*c2c26c8bSAndroid Build Coastguard Worker #endif
327*c2c26c8bSAndroid Build Coastguard Worker }
328*c2c26c8bSAndroid Build Coastguard Worker 
lease_set_hwaddr(struct dhcp_lease * lease,unsigned char * hwaddr,unsigned char * clid,int hw_len,int hw_type,int clid_len)329*c2c26c8bSAndroid Build Coastguard Worker void lease_set_hwaddr(struct dhcp_lease* lease, unsigned char* hwaddr, unsigned char* clid,
330*c2c26c8bSAndroid Build Coastguard Worker                       int hw_len, int hw_type, int clid_len) {
331*c2c26c8bSAndroid Build Coastguard Worker     if (hw_len != lease->hwaddr_len || hw_type != lease->hwaddr_type ||
332*c2c26c8bSAndroid Build Coastguard Worker         (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) {
333*c2c26c8bSAndroid Build Coastguard Worker         memcpy(lease->hwaddr, hwaddr, hw_len);
334*c2c26c8bSAndroid Build Coastguard Worker         lease->hwaddr_len = hw_len;
335*c2c26c8bSAndroid Build Coastguard Worker         lease->hwaddr_type = hw_type;
336*c2c26c8bSAndroid Build Coastguard Worker         lease->changed = file_dirty = 1; /* run script on change */
337*c2c26c8bSAndroid Build Coastguard Worker     }
338*c2c26c8bSAndroid Build Coastguard Worker 
339*c2c26c8bSAndroid Build Coastguard Worker     /* only update clid when one is available, stops packets
340*c2c26c8bSAndroid Build Coastguard Worker        without a clid removing the record. Lease init uses
341*c2c26c8bSAndroid Build Coastguard Worker        clid_len == 0 for no clid. */
342*c2c26c8bSAndroid Build Coastguard Worker     if (clid_len != 0 && clid) {
343*c2c26c8bSAndroid Build Coastguard Worker         if (!lease->clid) lease->clid_len = 0;
344*c2c26c8bSAndroid Build Coastguard Worker 
345*c2c26c8bSAndroid Build Coastguard Worker         if (lease->clid_len != clid_len) {
346*c2c26c8bSAndroid Build Coastguard Worker             lease->aux_changed = file_dirty = 1;
347*c2c26c8bSAndroid Build Coastguard Worker             free(lease->clid);
348*c2c26c8bSAndroid Build Coastguard Worker             if (!(lease->clid = whine_malloc(clid_len))) return;
349*c2c26c8bSAndroid Build Coastguard Worker         } else if (memcmp(lease->clid, clid, clid_len) != 0)
350*c2c26c8bSAndroid Build Coastguard Worker             lease->aux_changed = file_dirty = 1;
351*c2c26c8bSAndroid Build Coastguard Worker 
352*c2c26c8bSAndroid Build Coastguard Worker         lease->clid_len = clid_len;
353*c2c26c8bSAndroid Build Coastguard Worker         memcpy(lease->clid, clid, clid_len);
354*c2c26c8bSAndroid Build Coastguard Worker     }
355*c2c26c8bSAndroid Build Coastguard Worker }
356*c2c26c8bSAndroid Build Coastguard Worker 
kill_name(struct dhcp_lease * lease)357*c2c26c8bSAndroid Build Coastguard Worker static void kill_name(struct dhcp_lease* lease) {
358*c2c26c8bSAndroid Build Coastguard Worker     /* run script to say we lost our old name */
359*c2c26c8bSAndroid Build Coastguard Worker 
360*c2c26c8bSAndroid Build Coastguard Worker     /* this shouldn't happen unless updates are very quick and the
361*c2c26c8bSAndroid Build Coastguard Worker        script very slow, we just avoid a memory leak if it does. */
362*c2c26c8bSAndroid Build Coastguard Worker     free(lease->old_hostname);
363*c2c26c8bSAndroid Build Coastguard Worker 
364*c2c26c8bSAndroid Build Coastguard Worker     /* If we know the fqdn, pass that. The helper will derive the
365*c2c26c8bSAndroid Build Coastguard Worker        unqualified name from it, free the unqulaified name here. */
366*c2c26c8bSAndroid Build Coastguard Worker 
367*c2c26c8bSAndroid Build Coastguard Worker     if (lease->fqdn) {
368*c2c26c8bSAndroid Build Coastguard Worker         lease->old_hostname = lease->fqdn;
369*c2c26c8bSAndroid Build Coastguard Worker         free(lease->hostname);
370*c2c26c8bSAndroid Build Coastguard Worker     } else
371*c2c26c8bSAndroid Build Coastguard Worker         lease->old_hostname = lease->hostname;
372*c2c26c8bSAndroid Build Coastguard Worker 
373*c2c26c8bSAndroid Build Coastguard Worker     lease->hostname = lease->fqdn = NULL;
374*c2c26c8bSAndroid Build Coastguard Worker }
375*c2c26c8bSAndroid Build Coastguard Worker 
lease_set_hostname(struct dhcp_lease * lease,char * name,int auth)376*c2c26c8bSAndroid Build Coastguard Worker void lease_set_hostname(struct dhcp_lease* lease, char* name, int auth) {
377*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease_tmp;
378*c2c26c8bSAndroid Build Coastguard Worker     char *new_name = NULL, *new_fqdn = NULL;
379*c2c26c8bSAndroid Build Coastguard Worker 
380*c2c26c8bSAndroid Build Coastguard Worker     if (lease->hostname && name && hostname_isequal(lease->hostname, name)) {
381*c2c26c8bSAndroid Build Coastguard Worker         lease->auth_name = auth;
382*c2c26c8bSAndroid Build Coastguard Worker         return;
383*c2c26c8bSAndroid Build Coastguard Worker     }
384*c2c26c8bSAndroid Build Coastguard Worker 
385*c2c26c8bSAndroid Build Coastguard Worker     if (!name && !lease->hostname) return;
386*c2c26c8bSAndroid Build Coastguard Worker 
387*c2c26c8bSAndroid Build Coastguard Worker     /* If a machine turns up on a new net without dropping the old lease,
388*c2c26c8bSAndroid Build Coastguard Worker        or two machines claim the same name, then we end up with two interfaces with
389*c2c26c8bSAndroid Build Coastguard Worker        the same name. Check for that here and remove the name from the old lease.
390*c2c26c8bSAndroid Build Coastguard Worker        Don't allow a name from the client to override a name from dnsmasq config. */
391*c2c26c8bSAndroid Build Coastguard Worker 
392*c2c26c8bSAndroid Build Coastguard Worker     if (name) {
393*c2c26c8bSAndroid Build Coastguard Worker         if ((new_name = whine_malloc(strlen(name) + 1))) {
394*c2c26c8bSAndroid Build Coastguard Worker             char* suffix = get_domain(lease->addr);
395*c2c26c8bSAndroid Build Coastguard Worker             strcpy(new_name, name);
396*c2c26c8bSAndroid Build Coastguard Worker             if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2))) {
397*c2c26c8bSAndroid Build Coastguard Worker                 strcpy(new_fqdn, name);
398*c2c26c8bSAndroid Build Coastguard Worker                 strcat(new_fqdn, ".");
399*c2c26c8bSAndroid Build Coastguard Worker                 strcat(new_fqdn, suffix);
400*c2c26c8bSAndroid Build Coastguard Worker             }
401*c2c26c8bSAndroid Build Coastguard Worker         }
402*c2c26c8bSAndroid Build Coastguard Worker 
403*c2c26c8bSAndroid Build Coastguard Worker         /* Depending on mode, we check either unqualified name or FQDN. */
404*c2c26c8bSAndroid Build Coastguard Worker         for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next) {
405*c2c26c8bSAndroid Build Coastguard Worker             if (daemon->options & OPT_DHCP_FQDN) {
406*c2c26c8bSAndroid Build Coastguard Worker                 if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn))
407*c2c26c8bSAndroid Build Coastguard Worker                     continue;
408*c2c26c8bSAndroid Build Coastguard Worker             } else {
409*c2c26c8bSAndroid Build Coastguard Worker                 if (!new_name || !lease_tmp->hostname ||
410*c2c26c8bSAndroid Build Coastguard Worker                     !hostname_isequal(lease_tmp->hostname, new_name))
411*c2c26c8bSAndroid Build Coastguard Worker                     continue;
412*c2c26c8bSAndroid Build Coastguard Worker             }
413*c2c26c8bSAndroid Build Coastguard Worker 
414*c2c26c8bSAndroid Build Coastguard Worker             if (lease_tmp->auth_name && !auth) {
415*c2c26c8bSAndroid Build Coastguard Worker                 free(new_name);
416*c2c26c8bSAndroid Build Coastguard Worker                 free(new_fqdn);
417*c2c26c8bSAndroid Build Coastguard Worker                 return;
418*c2c26c8bSAndroid Build Coastguard Worker             }
419*c2c26c8bSAndroid Build Coastguard Worker 
420*c2c26c8bSAndroid Build Coastguard Worker             kill_name(lease_tmp);
421*c2c26c8bSAndroid Build Coastguard Worker             break;
422*c2c26c8bSAndroid Build Coastguard Worker         }
423*c2c26c8bSAndroid Build Coastguard Worker     }
424*c2c26c8bSAndroid Build Coastguard Worker 
425*c2c26c8bSAndroid Build Coastguard Worker     if (lease->hostname) kill_name(lease);
426*c2c26c8bSAndroid Build Coastguard Worker 
427*c2c26c8bSAndroid Build Coastguard Worker     lease->hostname = new_name;
428*c2c26c8bSAndroid Build Coastguard Worker     lease->fqdn = new_fqdn;
429*c2c26c8bSAndroid Build Coastguard Worker     lease->auth_name = auth;
430*c2c26c8bSAndroid Build Coastguard Worker 
431*c2c26c8bSAndroid Build Coastguard Worker     file_dirty = 1;
432*c2c26c8bSAndroid Build Coastguard Worker     dns_dirty = 1;
433*c2c26c8bSAndroid Build Coastguard Worker     lease->changed = 1; /* run script on change */
434*c2c26c8bSAndroid Build Coastguard Worker }
435*c2c26c8bSAndroid Build Coastguard Worker 
lease_set_interface(struct dhcp_lease * lease,int interface)436*c2c26c8bSAndroid Build Coastguard Worker void lease_set_interface(struct dhcp_lease* lease, int interface) {
437*c2c26c8bSAndroid Build Coastguard Worker     if (lease->last_interface == interface) return;
438*c2c26c8bSAndroid Build Coastguard Worker 
439*c2c26c8bSAndroid Build Coastguard Worker     lease->last_interface = interface;
440*c2c26c8bSAndroid Build Coastguard Worker     lease->changed = 1;
441*c2c26c8bSAndroid Build Coastguard Worker }
442*c2c26c8bSAndroid Build Coastguard Worker 
rerun_scripts(void)443*c2c26c8bSAndroid Build Coastguard Worker void rerun_scripts(void) {
444*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
445*c2c26c8bSAndroid Build Coastguard Worker 
446*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases; lease; lease = lease->next) lease->changed = 1;
447*c2c26c8bSAndroid Build Coastguard Worker }
448*c2c26c8bSAndroid Build Coastguard Worker 
449*c2c26c8bSAndroid Build Coastguard Worker /* deleted leases get transferred to the old_leases list.
450*c2c26c8bSAndroid Build Coastguard Worker    remove them here, after calling the lease change
451*c2c26c8bSAndroid Build Coastguard Worker    script. Also run the lease change script on new/modified leases.
452*c2c26c8bSAndroid Build Coastguard Worker 
453*c2c26c8bSAndroid Build Coastguard Worker    Return zero if nothing to do. */
do_script_run(time_t now)454*c2c26c8bSAndroid Build Coastguard Worker int do_script_run(time_t now) {
455*c2c26c8bSAndroid Build Coastguard Worker     struct dhcp_lease* lease;
456*c2c26c8bSAndroid Build Coastguard Worker 
457*c2c26c8bSAndroid Build Coastguard Worker     if (old_leases) {
458*c2c26c8bSAndroid Build Coastguard Worker         lease = old_leases;
459*c2c26c8bSAndroid Build Coastguard Worker 
460*c2c26c8bSAndroid Build Coastguard Worker         /* If the lease still has an old_hostname, do the "old" action on that first */
461*c2c26c8bSAndroid Build Coastguard Worker         if (lease->old_hostname) {
462*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_SCRIPT
463*c2c26c8bSAndroid Build Coastguard Worker             queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
464*c2c26c8bSAndroid Build Coastguard Worker #endif
465*c2c26c8bSAndroid Build Coastguard Worker             free(lease->old_hostname);
466*c2c26c8bSAndroid Build Coastguard Worker             lease->old_hostname = NULL;
467*c2c26c8bSAndroid Build Coastguard Worker             return 1;
468*c2c26c8bSAndroid Build Coastguard Worker         } else {
469*c2c26c8bSAndroid Build Coastguard Worker             kill_name(lease);
470*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_SCRIPT
471*c2c26c8bSAndroid Build Coastguard Worker             queue_script(ACTION_DEL, lease, lease->old_hostname, now);
472*c2c26c8bSAndroid Build Coastguard Worker #endif
473*c2c26c8bSAndroid Build Coastguard Worker             old_leases = lease->next;
474*c2c26c8bSAndroid Build Coastguard Worker 
475*c2c26c8bSAndroid Build Coastguard Worker             free(lease->old_hostname);
476*c2c26c8bSAndroid Build Coastguard Worker             free(lease->clid);
477*c2c26c8bSAndroid Build Coastguard Worker             free(lease->vendorclass);
478*c2c26c8bSAndroid Build Coastguard Worker             free(lease->userclass);
479*c2c26c8bSAndroid Build Coastguard Worker             free(lease->supplied_hostname);
480*c2c26c8bSAndroid Build Coastguard Worker             free(lease);
481*c2c26c8bSAndroid Build Coastguard Worker 
482*c2c26c8bSAndroid Build Coastguard Worker             return 1;
483*c2c26c8bSAndroid Build Coastguard Worker         }
484*c2c26c8bSAndroid Build Coastguard Worker     }
485*c2c26c8bSAndroid Build Coastguard Worker 
486*c2c26c8bSAndroid Build Coastguard Worker     /* make sure we announce the loss of a hostname before its new location. */
487*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases; lease; lease = lease->next)
488*c2c26c8bSAndroid Build Coastguard Worker         if (lease->old_hostname) {
489*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_SCRIPT
490*c2c26c8bSAndroid Build Coastguard Worker             queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
491*c2c26c8bSAndroid Build Coastguard Worker #endif
492*c2c26c8bSAndroid Build Coastguard Worker             free(lease->old_hostname);
493*c2c26c8bSAndroid Build Coastguard Worker             lease->old_hostname = NULL;
494*c2c26c8bSAndroid Build Coastguard Worker             return 1;
495*c2c26c8bSAndroid Build Coastguard Worker         }
496*c2c26c8bSAndroid Build Coastguard Worker 
497*c2c26c8bSAndroid Build Coastguard Worker     for (lease = leases; lease; lease = lease->next)
498*c2c26c8bSAndroid Build Coastguard Worker         if (lease->new || lease->changed ||
499*c2c26c8bSAndroid Build Coastguard Worker             (lease->aux_changed && (daemon->options & OPT_LEASE_RO))) {
500*c2c26c8bSAndroid Build Coastguard Worker #ifdef HAVE_SCRIPT
501*c2c26c8bSAndroid Build Coastguard Worker             queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease,
502*c2c26c8bSAndroid Build Coastguard Worker                          lease->fqdn ? lease->fqdn : lease->hostname, now);
503*c2c26c8bSAndroid Build Coastguard Worker #endif
504*c2c26c8bSAndroid Build Coastguard Worker             lease->new = lease->changed = lease->aux_changed = 0;
505*c2c26c8bSAndroid Build Coastguard Worker 
506*c2c26c8bSAndroid Build Coastguard Worker             /* these are used for the "add" call, then junked, since they're not in the database */
507*c2c26c8bSAndroid Build Coastguard Worker             free(lease->vendorclass);
508*c2c26c8bSAndroid Build Coastguard Worker             lease->vendorclass = NULL;
509*c2c26c8bSAndroid Build Coastguard Worker 
510*c2c26c8bSAndroid Build Coastguard Worker             free(lease->userclass);
511*c2c26c8bSAndroid Build Coastguard Worker             lease->userclass = NULL;
512*c2c26c8bSAndroid Build Coastguard Worker 
513*c2c26c8bSAndroid Build Coastguard Worker             free(lease->supplied_hostname);
514*c2c26c8bSAndroid Build Coastguard Worker             lease->supplied_hostname = NULL;
515*c2c26c8bSAndroid Build Coastguard Worker 
516*c2c26c8bSAndroid Build Coastguard Worker             return 1;
517*c2c26c8bSAndroid Build Coastguard Worker         }
518*c2c26c8bSAndroid Build Coastguard Worker 
519*c2c26c8bSAndroid Build Coastguard Worker     return 0; /* nothing to do */
520*c2c26c8bSAndroid Build Coastguard Worker }
521*c2c26c8bSAndroid Build Coastguard Worker 
522*c2c26c8bSAndroid Build Coastguard Worker #endif
523