2 * Copyright (C) 2012 Steven Barth <steven@midlink.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License v2 as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
20 #include <arpa/inet.h>
21 #include <netinet/in.h>
25 static const char hexdigits
[] = "0123456789abcdef";
26 static const char hexvals
[] = {
27 -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1,
28 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
29 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
31 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
39 static char *argv
[4] = {NULL
, NULL
, NULL
, NULL
};
42 int script_init(const char *path
, const char *ifname
)
44 argv
[0] = (char*)path
;
45 argv
[1] = (char*)ifname
;
50 ssize_t
script_unhexlify(uint8_t *dst
, size_t len
, const char *src
)
53 for (c
= 0; c
< len
&& src
[0] && src
[1]; ++c
) {
54 int8_t x
= (int8_t)*src
++;
55 int8_t y
= (int8_t)*src
++;
56 if (x
< 0 || (x
= hexvals
[x
]) < 0
57 || y
< 0 || (y
= hexvals
[y
]) < 0)
60 while (*src
< 0 || (*src
&& hexvals
[(uint8_t)*src
] < 0))
68 void script_hexlify(char *dst
, const uint8_t *src
, size_t len
) {
69 for (size_t i
= 0; i
< len
; ++i
) {
70 *dst
++ = hexdigits
[src
[i
] >> 4];
71 *dst
++ = hexdigits
[src
[i
] & 0x0f];
77 static void ipv6_to_env(const char *name
,
78 const struct in6_addr
*addr
, size_t cnt
)
80 size_t buf_len
= strlen(name
);
81 char *buf
= realloc(NULL
, cnt
* INET6_ADDRSTRLEN
+ buf_len
+ 2);
82 memcpy(buf
, name
, buf_len
);
84 for (size_t i
= 0; i
< cnt
; ++i
) {
85 inet_ntop(AF_INET6
, &addr
[i
], &buf
[buf_len
], INET6_ADDRSTRLEN
);
86 buf_len
+= strlen(&buf
[buf_len
]);
89 buf
[buf_len
- 1] = '\0';
94 static void fqdn_to_env(const char *name
, const uint8_t *fqdn
, size_t len
)
96 size_t buf_len
= strlen(name
);
97 const uint8_t *fqdn_end
= fqdn
+ len
;
98 char *buf
= realloc(NULL
, len
+ buf_len
+ 2);
99 memcpy(buf
, name
, buf_len
);
100 buf
[buf_len
++] = '=';
102 while (l
> 0 && fqdn
< fqdn_end
) {
103 l
= dn_expand(fqdn
, &fqdn
[len
], fqdn
, &buf
[buf_len
], len
);
105 buf_len
+= strlen(&buf
[buf_len
]);
106 buf
[buf_len
++] = ' ';
108 buf
[buf_len
- 1] = '\0';
113 static void bin_to_env(uint8_t *opts
, size_t len
)
115 uint8_t *oend
= opts
+ len
, *odata
;
116 uint16_t otype
, olen
;
117 dhcpv6_for_each_option(opts
, oend
, otype
, olen
, odata
) {
118 char *buf
= realloc(NULL
, 14 + (olen
* 2));
121 snprintf(buf
, 14, "OPTION_%hu=", otype
);
122 buf_len
+= strlen(buf
);
124 script_hexlify(&buf
[buf_len
], odata
, olen
);
130 static void prefix_to_env(const char *name
, const uint8_t *fqdn
, size_t len
)
132 size_t buf_len
= strlen(name
);
133 struct dhcpv6_ia_prefix
*p
= NULL
;
134 char *buf
= realloc(NULL
, buf_len
+ 2 +
135 (len
/ sizeof(*p
)) * (INET6_ADDRSTRLEN
+ 32));
136 memcpy(buf
, name
, buf_len
);
137 buf
[buf_len
++] = '=';
139 uint16_t otype
, olen
;
141 dhcpv6_for_each_option(fqdn
, &fqdn
[len
], otype
, olen
, odata
) {
142 if (otype
!= DHCPV6_OPT_IA_PREFIX
|| olen
+ 4U < sizeof(*p
))
145 p
= (struct dhcpv6_ia_prefix
*)&odata
[-4];
146 inet_ntop(AF_INET6
, &p
->addr
, &buf
[buf_len
], INET6_ADDRSTRLEN
);
147 buf_len
+= strlen(&buf
[buf_len
]);
148 buf_len
+= snprintf(&buf
[buf_len
], 32, "/%hhu,%u,%u ",
149 p
->prefix
, ntohl(p
->preferred
),
152 buf
[buf_len
- 1] = '\0';
157 void script_call(const char *status
)
159 size_t dns_len
, search_len
, custom_len
;
160 struct in6_addr
*dns
= odhcp6c_get_state(STATE_DNS
, &dns_len
);
161 uint8_t *search
= odhcp6c_get_state(STATE_SEARCH
, &search_len
);
162 uint8_t *custom
= odhcp6c_get_state(STATE_CUSTOM_OPTS
, &custom_len
);
164 size_t prefix_len
, lost_pd_len
;
165 uint8_t *prefix
= odhcp6c_get_state(STATE_IA_PD
, &prefix_len
);
166 uint8_t *lost_pd
= odhcp6c_get_state(STATE_IA_PD_LOST
, &lost_pd_len
);
168 // Don't set environment before forking, because env is leaky.
170 ipv6_to_env("RDNSS", dns
, dns_len
/ sizeof(*dns
));
171 fqdn_to_env("DOMAINS", search
, search_len
);
172 bin_to_env(custom
, custom_len
);
173 prefix_to_env("PREFIXES", prefix
, prefix_len
);
174 prefix_to_env("PREFIXES_LOST", lost_pd
, lost_pd_len
);
176 argv
[2] = (char*)status
;
177 execv(argv
[0], argv
);
181 // Delete lost prefixes and user opts
182 odhcp6c_clear_state(STATE_IA_PD_LOST
);
183 odhcp6c_clear_state(STATE_CUSTOM_OPTS
);